The number of characters to be copied must be one less than the buffer size to accoun...
[TortoiseGit.git] / src / Utils / PathUtils.cpp
blob676058d1937f0bd3628e1250ac4e0dbb1b299a4e
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2014 - TortoiseGit
4 // Copyright (C) 2003-2008, 2013-2014 - 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 std::unique_ptr<TCHAR[]> buf(new TCHAR[len]);
28 std::unique_ptr<TCHAR[]> internalpathbuf(new TCHAR[len]);
29 TCHAR * pPath = internalpathbuf.get();
30 SECURITY_ATTRIBUTES attribs;
32 SecureZeroMemory(&attribs, sizeof(SECURITY_ATTRIBUTES));
34 attribs.nLength = sizeof(SECURITY_ATTRIBUTES);
35 attribs.bInheritHandle = FALSE;
37 ConvertToBackslash(internalpathbuf.get(), path, len);
40 SecureZeroMemory(buf.get(), (len)*sizeof(TCHAR));
41 TCHAR * slashpos = _tcschr(pPath, '\\');
42 if (slashpos)
43 _tcsncpy_s(buf.get(), len, internalpathbuf.get(), slashpos - internalpathbuf.get());
44 else
45 _tcsncpy_s(buf.get(), len, internalpathbuf.get(), len - 1);
46 CreateDirectory(buf.get(), &attribs);
47 pPath = _tcschr(pPath, '\\');
48 } while ((pPath++)&&(_tcschr(pPath, '\\')));
50 return CreateDirectory(internalpathbuf.get(), &attribs);
53 void CPathUtils::Unescape(char * psz)
55 char * pszSource = psz;
56 char * pszDest = psz;
58 static const char szHex[] = "0123456789ABCDEF";
60 // Unescape special characters. The number of characters
61 // in the *pszDest is assumed to be <= the number of characters
62 // in pszSource (they are both the same string anyway)
64 while (*pszSource != '\0' && *pszDest != '\0')
66 if (*pszSource == '%')
68 // The next two chars following '%' should be digits
69 if ( *(pszSource + 1) == '\0' ||
70 *(pszSource + 2) == '\0' )
72 // nothing left to do
73 break;
76 char nValue = '?';
77 const char * pszLow = NULL;
78 const char * pszHigh = NULL;
79 ++pszSource;
81 *pszSource = (char) toupper(*pszSource);
82 pszHigh = strchr(szHex, *pszSource);
84 if (pszHigh != NULL)
86 ++pszSource;
87 *pszSource = (char) toupper(*pszSource);
88 pszLow = strchr(szHex, *pszSource);
90 if (pszLow != NULL)
92 nValue = (char) (((pszHigh - szHex) << 4) +
93 (pszLow - szHex));
96 else
98 pszSource--;
99 nValue = *pszSource;
101 *pszDest++ = nValue;
103 else
104 *pszDest++ = *pszSource;
106 ++pszSource;
109 *pszDest = '\0';
112 static const char iri_escape_chars[256] = {
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,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
118 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
119 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
120 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
122 /* 128 */
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,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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
133 const char uri_autoescape_chars[256] = {
134 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
135 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
136 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
137 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
139 /* 64 */
140 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
141 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
142 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
143 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
145 /* 128 */
146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
151 /* 192 */
152 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
153 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
158 static const char uri_char_validity[256] = {
159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
160 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
162 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
164 /* 64 */
165 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
166 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
167 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
168 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
170 /* 128 */
171 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
176 /* 192 */
177 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
184 void CPathUtils::ConvertToBackslash(LPTSTR dest, LPCTSTR src, size_t len)
186 _tcscpy_s(dest, len, src);
187 TCHAR * p = dest;
188 for (; *p != '\0'; ++p)
189 if (*p == '/')
190 *p = '\\';
193 CStringA CPathUtils::PathEscape(const CStringA& path)
195 CStringA ret2;
196 int c;
197 int i;
198 for (i=0; path[i]; ++i)
200 c = (unsigned char)path[i];
201 if (iri_escape_chars[c])
203 // no escaping needed for that char
204 ret2 += (unsigned char)path[i];
206 else
208 // char needs escaping
209 CStringA temp;
210 temp.Format("%%%02X", (unsigned char)c);
211 ret2 += temp;
214 CStringA ret;
215 for (i=0; ret2[i]; ++i)
217 c = (unsigned char)ret2[i];
218 if (uri_autoescape_chars[c])
220 // no escaping needed for that char
221 ret += (unsigned char)ret2[i];
223 else
225 // char needs escaping
226 CStringA temp;
227 temp.Format("%%%02X", (unsigned char)c);
228 ret += temp;
232 if ((ret.Left(11).Compare("file:///%5C") == 0) && (ret.Find('%', 12) < 0))
233 ret.Replace(("file:///%5C"), ("file://"));
234 ret.Replace(("file:////%5C"), ("file://"));
236 return ret;
239 #ifdef CSTRING_AVAILABLE
240 CString CPathUtils::GetFileNameFromPath(CString sPath)
242 CString ret;
243 sPath.Replace(_T("/"), _T("\\"));
244 ret = sPath.Mid(sPath.ReverseFind('\\') + 1);
245 return ret;
248 CString CPathUtils::GetFileExtFromPath(const CString& sPath)
250 int dotPos = sPath.ReverseFind('.');
251 int slashPos = sPath.ReverseFind('\\');
252 if (slashPos < 0)
253 slashPos = sPath.ReverseFind('/');
254 if (dotPos > slashPos)
255 return sPath.Mid(dotPos);
256 return CString();
259 CString CPathUtils::GetLongPathname(const CString& path)
261 if (path.IsEmpty())
262 return path;
263 TCHAR pathbufcanonicalized[MAX_PATH] = {0}; // MAX_PATH ok.
264 DWORD ret = 0;
265 CString sRet;
266 if (!PathIsURL(path) && PathIsRelative(path))
268 ret = GetFullPathName(path, 0, NULL, NULL);
269 if (ret)
271 std::unique_ptr<TCHAR[]> pathbuf(new TCHAR[ret + 1]);
272 if ((ret = GetFullPathName(path, ret, pathbuf.get(), NULL)) != 0)
273 sRet = CString(pathbuf.get(), ret);
276 else if (PathCanonicalize(pathbufcanonicalized, path))
278 ret = ::GetLongPathName(pathbufcanonicalized, NULL, 0);
279 if (ret == 0)
280 return path;
281 std::unique_ptr<TCHAR[]> pathbuf(new TCHAR[ret + 2]);
282 ret = ::GetLongPathName(pathbufcanonicalized, pathbuf.get(), ret + 1);
283 sRet = CString(pathbuf.get(), ret);
285 else
287 ret = ::GetLongPathName(path, NULL, 0);
288 if (ret == 0)
289 return path;
290 std::unique_ptr<TCHAR[]> pathbuf(new TCHAR[ret + 2]);
291 ret = ::GetLongPathName(path, pathbuf.get(), ret + 1);
292 sRet = CString(pathbuf.get(), ret);
294 if (ret == 0)
295 return path;
296 return sRet;
299 BOOL CPathUtils::FileCopy(CString srcPath, CString destPath, BOOL force)
301 srcPath.Replace('/', '\\');
302 destPath.Replace('/', '\\');
303 CString destFolder = destPath.Left(destPath.ReverseFind('\\'));
304 MakeSureDirectoryPathExists(destFolder);
305 return (CopyFile(srcPath, destPath, !force));
308 CString CPathUtils::ParsePathInString(const CString& Str)
310 CString sToken;
311 int curPos = 0;
312 sToken = Str.Tokenize(_T("'\t\r\n"), curPos);
313 while (!sToken.IsEmpty())
315 if ((sToken.Find('/')>=0)||(sToken.Find('\\')>=0))
317 sToken.Trim(_T("'\""));
318 return sToken;
320 sToken = Str.Tokenize(_T("'\t\r\n"), curPos);
322 sToken.Empty();
323 return sToken;
326 CString CPathUtils::GetAppDirectory(HMODULE hMod /* = NULL */)
328 CString path;
329 DWORD len = 0;
330 DWORD bufferlen = MAX_PATH; // MAX_PATH is not the limit here!
331 path.GetBuffer(bufferlen);
334 bufferlen += MAX_PATH; // MAX_PATH is not the limit here!
335 path.ReleaseBuffer(0);
336 len = GetModuleFileName(hMod, path.GetBuffer(bufferlen+1), bufferlen);
337 } while(len == bufferlen);
338 path.ReleaseBuffer();
339 path = path.Left(path.ReverseFind('\\')+1);
340 return GetLongPathname(path);
343 CString CPathUtils::GetAppParentDirectory(HMODULE hMod /* = NULL */)
345 CString path = GetAppDirectory(hMod);
346 path = path.Left(path.ReverseFind('\\'));
347 path = path.Left(path.ReverseFind('\\')+1);
348 return path;
351 CString CPathUtils::GetAppDataDirectory()
353 TCHAR path[MAX_PATH] = { 0 }; //MAX_PATH ok here.
354 if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path)!=S_OK)
355 return CString();
357 _tcscat_s(path, MAX_PATH, _T("\\TortoiseGit"));
358 if (!PathIsDirectory(path))
359 CreateDirectory(path, NULL);
361 return CString (path) + _T('\\');
364 CString CPathUtils::GetLocalAppDataDirectory()
366 TCHAR path[MAX_PATH] = { 0 }; //MAX_PATH ok here.
367 if (SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path) != S_OK)
368 return CString();
370 _tcscat_s(path, MAX_PATH, _T("\\TortoiseGit"));
371 if (!PathIsDirectory(path))
372 CreateDirectory(path, NULL);
374 return CString(path) + _T('\\');
377 CStringA CPathUtils::PathUnescape(const CStringA& path)
379 std::unique_ptr<char[]> urlabuf (new char[path.GetLength() + 1]);
381 strcpy_s(urlabuf.get(), path.GetLength()+1, path);
382 Unescape(urlabuf.get());
384 return urlabuf.get();
387 CStringW CPathUtils::PathUnescape(const CStringW& path)
389 char * buf;
390 CStringA patha;
391 int len = path.GetLength();
392 if (len==0)
393 return CStringW();
394 buf = patha.GetBuffer(len*4 + 1);
395 int lengthIncTerminator = WideCharToMultiByte(CP_UTF8, 0, path, -1, buf, len*4, NULL, NULL);
396 patha.ReleaseBuffer(lengthIncTerminator-1);
398 patha = PathUnescape(patha);
400 WCHAR * bufw;
401 len = patha.GetLength();
402 bufw = new WCHAR[len*4 + 1];
403 SecureZeroMemory(bufw, (len*4 + 1)*sizeof(WCHAR));
404 MultiByteToWideChar(CP_UTF8, 0, patha, -1, bufw, len*4);
405 CStringW ret = CStringW(bufw);
406 delete [] bufw;
407 return ret;
409 #ifdef _MFC_VER
410 CString CPathUtils::GetVersionFromFile(const CString & p_strFilename)
412 struct TRANSARRAY
414 WORD wLanguageID;
415 WORD wCharacterSet;
418 CString strReturn;
419 DWORD dwReserved = 0;
420 DWORD dwBufferSize = GetFileVersionInfoSize((LPTSTR)(LPCTSTR)p_strFilename,&dwReserved);
422 if (dwBufferSize > 0)
424 LPVOID pBuffer = (void*) malloc(dwBufferSize);
426 if (pBuffer != (void*) NULL)
428 UINT nInfoSize = 0,
429 nFixedLength = 0;
430 LPSTR lpVersion = NULL;
431 VOID* lpFixedPointer;
432 TRANSARRAY* lpTransArray;
433 CString strLangProductVersion;
435 GetFileVersionInfo((LPTSTR)(LPCTSTR)p_strFilename,
436 dwReserved,
437 dwBufferSize,
438 pBuffer);
440 // Check the current language
441 VerQueryValue( pBuffer,
442 _T("\\VarFileInfo\\Translation"),
443 &lpFixedPointer,
444 &nFixedLength);
445 lpTransArray = (TRANSARRAY*) lpFixedPointer;
447 strLangProductVersion.Format(_T("\\StringFileInfo\\%04x%04x\\ProductVersion"),
448 lpTransArray[0].wLanguageID, lpTransArray[0].wCharacterSet);
450 VerQueryValue(pBuffer,
451 (LPTSTR)(LPCTSTR)strLangProductVersion,
452 (LPVOID *)&lpVersion,
453 &nInfoSize);
454 if (nInfoSize && lpVersion)
455 strReturn = (LPCTSTR)lpVersion;
456 free(pBuffer);
460 return strReturn;
462 #endif
463 CString CPathUtils::PathPatternEscape(const CString& path)
465 CString result = path;
466 // first remove already escaped patterns to avoid having those
467 // escaped twice
468 result.Replace(_T("\\["), _T("["));
469 result.Replace(_T("\\]"), _T("]"));
470 // now escape the patterns (again)
471 result.Replace(_T("["), _T("\\["));
472 result.Replace(_T("]"), _T("\\]"));
473 return result;
476 CString CPathUtils::PathPatternUnEscape(const CString& path)
478 CString result = path;
479 result.Replace(_T("\\["), _T("["));
480 result.Replace(_T("\\]"), _T("]"));
481 return result;
484 #endif