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.
21 #include "PathUtils.h"
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
, '\\');
43 _tcsncpy_s(buf
.get(), len
, internalpathbuf
.get(), slashpos
- internalpathbuf
.get());
45 _tcsncpy_s(buf
.get(), len
, internalpathbuf
.get(), len
);
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
;
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' )
77 const char * pszLow
= NULL
;
78 const char * pszHigh
= NULL
;
81 *pszSource
= (char) toupper(*pszSource
);
82 pszHigh
= strchr(szHex
, *pszSource
);
87 *pszSource
= (char) toupper(*pszSource
);
88 pszLow
= strchr(szHex
, *pszSource
);
92 nValue
= (char) (((pszHigh
- szHex
) << 4) +
104 *pszDest
++ = *pszSource
;
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,
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,
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,
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,
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,
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,
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,
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
);
188 for (; *p
!= '\0'; ++p
)
193 CStringA
CPathUtils::PathEscape(const CStringA
& path
)
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
];
208 // char needs escaping
210 temp
.Format("%%%02X", (unsigned char)c
);
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
];
225 // char needs escaping
227 temp
.Format("%%%02X", (unsigned char)c
);
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://"));
239 #ifdef CSTRING_AVAILABLE
240 CString
CPathUtils::GetFileNameFromPath(CString sPath
)
243 sPath
.Replace(_T("/"), _T("\\"));
244 ret
= sPath
.Mid(sPath
.ReverseFind('\\') + 1);
248 CString
CPathUtils::GetFileExtFromPath(const CString
& sPath
)
250 int dotPos
= sPath
.ReverseFind('.');
251 int slashPos
= sPath
.ReverseFind('\\');
253 slashPos
= sPath
.ReverseFind('/');
254 if (dotPos
> slashPos
)
255 return sPath
.Mid(dotPos
);
259 CString
CPathUtils::GetLongPathname(const CString
& path
)
263 TCHAR pathbufcanonicalized
[MAX_PATH
] = {0}; // MAX_PATH ok.
266 if (!PathIsURL(path
) && PathIsRelative(path
))
268 ret
= GetFullPathName(path
, 0, NULL
, NULL
);
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);
281 std::unique_ptr
<TCHAR
[]> pathbuf(new TCHAR
[ret
+ 2]);
282 ret
= ::GetLongPathName(pathbufcanonicalized
, pathbuf
.get(), ret
+ 1);
283 sRet
= CString(pathbuf
.get(), ret
);
287 ret
= ::GetLongPathName(path
, NULL
, 0);
290 std::unique_ptr
<TCHAR
[]> pathbuf(new TCHAR
[ret
+ 2]);
291 ret
= ::GetLongPathName(path
, pathbuf
.get(), ret
+ 1);
292 sRet
= CString(pathbuf
.get(), ret
);
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
)
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("'\""));
320 sToken
= Str
.Tokenize(_T("'\t\r\n"), curPos
);
326 CString
CPathUtils::GetAppDirectory(HMODULE hMod
/* = NULL */)
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);
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
)
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
)
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
)
391 int len
= path
.GetLength();
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
);
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
);
410 CString
CPathUtils::GetVersionFromFile(const CString
& p_strFilename
)
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
)
430 LPSTR lpVersion
= NULL
;
431 VOID
* lpFixedPointer
;
432 TRANSARRAY
* lpTransArray
;
433 CString strLangProductVersion
;
435 GetFileVersionInfo((LPTSTR
)(LPCTSTR
)p_strFilename
,
440 // Check the current language
441 VerQueryValue( pBuffer
,
442 _T("\\VarFileInfo\\Translation"),
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
,
454 if (nInfoSize
&& lpVersion
)
455 strReturn
= (LPCTSTR
)lpVersion
;
463 CString
CPathUtils::PathPatternEscape(const CString
& path
)
465 CString result
= path
;
466 // first remove already escaped patterns to avoid having those
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("\\]"));
476 CString
CPathUtils::PathPatternUnEscape(const CString
& path
)
478 CString result
= path
;
479 result
.Replace(_T("\\["), _T("["));
480 result
.Replace(_T("\\]"), _T("]"));