OSDN Git Service

Add ext and utils
[tortoisegit/TortoiseGitJp.git] / Git / TGitPath.cpp
1 // TortoiseGit - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseGit\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "StdAfx.h"\r
20 #include "TGitPath.h"\r
21 #include "UnicodeUtils.h"\r
22 #include "GitAdminDir.h"\r
23 #include "PathUtils.h"\r
24 #include <regex>\r
25 \r
26 #if defined(_MFC_VER)\r
27 //#include "MessageBox.h"\r
28 //#include "AppUtils.h"\r
29 #endif\r
30 \r
31 using namespace std;\r
32 \r
33 \r
34 CTGitPath::CTGitPath(void) :\r
35         m_bDirectoryKnown(false),\r
36         m_bIsDirectory(false),\r
37         m_bIsURL(false),\r
38         m_bURLKnown(false),\r
39         m_bHasAdminDirKnown(false),\r
40         m_bHasAdminDir(false),\r
41         m_bIsValidOnWindowsKnown(false),\r
42         m_bIsReadOnly(false),\r
43         m_bIsAdminDirKnown(false),\r
44         m_bIsAdminDir(false),\r
45         m_bExists(false),\r
46         m_bExistsKnown(false),\r
47         m_bLastWriteTimeKnown(0),\r
48         m_lastWriteTime(0),\r
49         m_customData(NULL),\r
50         m_bIsSpecialDirectoryKnown(false),\r
51         m_bIsSpecialDirectory(false)\r
52 {\r
53 }\r
54 \r
55 CTGitPath::~CTGitPath(void)\r
56 {\r
57 }\r
58 // Create a TGitPath object from an unknown path type (same as using SetFromUnknown)\r
59 CTGitPath::CTGitPath(const CString& sUnknownPath) :\r
60         m_bDirectoryKnown(false),\r
61         m_bIsDirectory(false),\r
62         m_bIsURL(false),\r
63         m_bURLKnown(false),\r
64         m_bHasAdminDirKnown(false),\r
65         m_bHasAdminDir(false),\r
66         m_bIsValidOnWindowsKnown(false),\r
67         m_bIsReadOnly(false),\r
68         m_bIsAdminDirKnown(false),\r
69         m_bIsAdminDir(false),\r
70         m_bExists(false),\r
71         m_bExistsKnown(false),\r
72         m_bLastWriteTimeKnown(0),\r
73         m_lastWriteTime(0),\r
74         m_customData(NULL),\r
75         m_bIsSpecialDirectoryKnown(false),\r
76         m_bIsSpecialDirectory(false)\r
77 {\r
78         SetFromUnknown(sUnknownPath);\r
79 }\r
80 \r
81 void CTGitPath::SetFromGit(const char* pPath)\r
82 {\r
83         Reset();\r
84         if (pPath == NULL)\r
85                 return;\r
86         int len = MultiByteToWideChar(CP_UTF8, 0, pPath, -1, NULL, 0);\r
87         if (len)\r
88         {\r
89                 len = MultiByteToWideChar(CP_UTF8, 0, pPath, -1, m_sFwdslashPath.GetBuffer(len+1), len+1);\r
90                 m_sFwdslashPath.ReleaseBuffer(len-1);\r
91         }\r
92         SanitizeRootPath(m_sFwdslashPath, true);\r
93 }\r
94 \r
95 void CTGitPath::SetFromGit(const char* pPath, bool bIsDirectory)\r
96 {\r
97         SetFromGit(pPath);\r
98         m_bDirectoryKnown = true;\r
99         m_bIsDirectory = bIsDirectory;\r
100 }\r
101 \r
102 void CTGitPath::SetFromGit(const CString& sPath)\r
103 {\r
104         Reset();\r
105         m_sFwdslashPath = sPath;\r
106         SanitizeRootPath(m_sFwdslashPath, true);\r
107 }\r
108 \r
109 void CTGitPath::SetFromWin(LPCTSTR pPath)\r
110 {\r
111         Reset();\r
112         m_sBackslashPath = pPath;\r
113         SanitizeRootPath(m_sBackslashPath, false);\r
114         ATLASSERT(m_sBackslashPath.Find('/')<0);\r
115 }\r
116 void CTGitPath::SetFromWin(const CString& sPath)\r
117 {\r
118         Reset();\r
119         m_sBackslashPath = sPath;\r
120         SanitizeRootPath(m_sBackslashPath, false);\r
121 }\r
122 void CTGitPath::SetFromWin(const CString& sPath, bool bIsDirectory)\r
123 {\r
124         Reset();\r
125         m_sBackslashPath = sPath;\r
126         m_bIsDirectory = bIsDirectory;\r
127         m_bDirectoryKnown = true;\r
128         SanitizeRootPath(m_sBackslashPath, false);\r
129 }\r
130 void CTGitPath::SetFromUnknown(const CString& sPath)\r
131 {\r
132         Reset();\r
133         // Just set whichever path we think is most likely to be used\r
134         SetFwdslashPath(sPath);\r
135 }\r
136 \r
137 LPCTSTR CTGitPath::GetWinPath() const\r
138 {\r
139         if(IsEmpty())\r
140         {\r
141                 return _T("");\r
142         }\r
143         if(m_sBackslashPath.IsEmpty())\r
144         {\r
145                 SetBackslashPath(m_sFwdslashPath);\r
146         }\r
147         return m_sBackslashPath;\r
148 }\r
149 // This is a temporary function, to be used during the migration to \r
150 // the path class.  Ultimately, functions consuming paths should take a CTGitPath&, not a CString\r
151 const CString& CTGitPath::GetWinPathString() const\r
152 {\r
153         if(m_sBackslashPath.IsEmpty())\r
154         {\r
155                 SetBackslashPath(m_sFwdslashPath);\r
156         }\r
157         return m_sBackslashPath;\r
158 }\r
159 \r
160 const CString& CTGitPath::GetGitPathString() const\r
161 {\r
162         if(m_sFwdslashPath.IsEmpty())\r
163         {\r
164                 SetFwdslashPath(m_sBackslashPath);\r
165         }\r
166         return m_sFwdslashPath;\r
167 }\r
168 \r
169 #if 0\r
170 const char* CTGitPath::GetGitApiPath(apr_pool_t *pool) const\r
171 {\r
172         // This funny-looking 'if' is to avoid a subtle problem with empty paths, whereby\r
173         // each call to GetGitApiPath returns a different pointer value.\r
174         // If you made multiple calls to GetGitApiPath on the same string, only the last\r
175         // one would give you a valid pointer to an empty string, because each \r
176         // call would invalidate the previous call's return. \r
177         if(IsEmpty())\r
178         {\r
179                 return "";\r
180         }\r
181         if(m_sFwdslashPath.IsEmpty())\r
182         {\r
183                 SetFwdslashPath(m_sBackslashPath);\r
184         }\r
185         if(m_sUTF8FwdslashPath.IsEmpty())\r
186         {\r
187                 SetUTF8FwdslashPath(m_sFwdslashPath);\r
188         }\r
189         if (svn_path_is_url(m_sUTF8FwdslashPath))\r
190         {\r
191                 m_sUTF8FwdslashPathEscaped = CPathUtils::PathEscape(m_sUTF8FwdslashPath);\r
192                 m_sUTF8FwdslashPathEscaped.Replace("file:////", "file:///\\");\r
193                 m_sUTF8FwdslashPathEscaped = svn_path_canonicalize(m_sUTF8FwdslashPathEscaped, pool);\r
194                 return m_sUTF8FwdslashPathEscaped;\r
195         }\r
196         m_sUTF8FwdslashPath = svn_path_canonicalize(m_sUTF8FwdslashPath, pool);\r
197 \r
198         return m_sUTF8FwdslashPath;\r
199 }\r
200 #endif\r
201 \r
202 const CString& CTGitPath::GetUIPathString() const\r
203 {\r
204         if (m_sUIPath.IsEmpty())\r
205         {\r
206 #if defined(_MFC_VER)\r
207                 //BUGBUG HORRIBLE!!! - CPathUtils::IsEscaped doesn't need to be MFC-only\r
208                 if (IsUrl())\r
209                 {\r
210                         m_sUIPath = CPathUtils::PathUnescape(GetGitPathString());\r
211                         m_sUIPath.Replace(_T("file:////"), _T("file:///\\"));\r
212 \r
213                 }\r
214                 else\r
215 #endif \r
216                 {\r
217                         m_sUIPath = GetWinPathString();\r
218                 }\r
219         }\r
220         return m_sUIPath;\r
221 }\r
222 \r
223 void CTGitPath::SetFwdslashPath(const CString& sPath) const\r
224 {\r
225         m_sFwdslashPath = sPath;\r
226         m_sFwdslashPath.Replace('\\', '/');\r
227 \r
228         // We don't leave a trailing /\r
229         m_sFwdslashPath.TrimRight('/'); \r
230 \r
231         SanitizeRootPath(m_sFwdslashPath, true);\r
232 \r
233         m_sFwdslashPath.Replace(_T("file:////"), _T("file:///\\"));\r
234 \r
235         m_sUTF8FwdslashPath.Empty();\r
236 }\r
237 \r
238 void CTGitPath::SetBackslashPath(const CString& sPath) const\r
239 {\r
240         m_sBackslashPath = sPath;\r
241         m_sBackslashPath.Replace('/', '\\');\r
242         m_sBackslashPath.TrimRight('\\');\r
243         SanitizeRootPath(m_sBackslashPath, false);\r
244 }\r
245 \r
246 void CTGitPath::SetUTF8FwdslashPath(const CString& sPath) const\r
247 {\r
248         m_sUTF8FwdslashPath = CUnicodeUtils::GetUTF8(sPath);\r
249 }\r
250 \r
251 void CTGitPath::SanitizeRootPath(CString& sPath, bool bIsForwardPath) const\r
252 {\r
253         // Make sure to add the trailing slash to root paths such as 'C:'\r
254         if (sPath.GetLength() == 2 && sPath[1] == ':')\r
255         {\r
256                 sPath += (bIsForwardPath) ? _T("/") : _T("\\");\r
257         }\r
258 }\r
259 \r
260 bool CTGitPath::IsUrl() const\r
261 {\r
262 #if 0\r
263         if (!m_bURLKnown)\r
264         {\r
265                 EnsureFwdslashPathSet();\r
266                 if(m_sUTF8FwdslashPath.IsEmpty())\r
267                 {\r
268                         SetUTF8FwdslashPath(m_sFwdslashPath);\r
269                 }\r
270                 m_bIsURL = !!svn_path_is_url(m_sUTF8FwdslashPath);\r
271                 m_bURLKnown = true;\r
272         }\r
273         return m_bIsURL;\r
274 #endif \r
275         return false;\r
276 }\r
277 \r
278 bool CTGitPath::IsDirectory() const\r
279 {\r
280         if(!m_bDirectoryKnown)\r
281         {\r
282                 UpdateAttributes();\r
283         }\r
284         return m_bIsDirectory;\r
285 }\r
286 \r
287 bool CTGitPath::Exists() const\r
288 {\r
289         if (!m_bExistsKnown)\r
290         {\r
291                 UpdateAttributes();\r
292         }\r
293         return m_bExists;\r
294 }\r
295 \r
296 bool CTGitPath::Delete(bool bTrash) const\r
297 {\r
298         EnsureBackslashPathSet();\r
299         ::SetFileAttributes(m_sBackslashPath, FILE_ATTRIBUTE_NORMAL);\r
300         bool bRet = false;\r
301         if (Exists())\r
302         {\r
303                 if ((bTrash)||(IsDirectory()))\r
304                 {\r
305                         TCHAR * buf = new TCHAR[m_sBackslashPath.GetLength()+2];\r
306                         _tcscpy_s(buf, m_sBackslashPath.GetLength()+2, m_sBackslashPath);\r
307                         buf[m_sBackslashPath.GetLength()] = 0;\r
308                         buf[m_sBackslashPath.GetLength()+1] = 0;\r
309                         SHFILEOPSTRUCT shop = {0};\r
310                         shop.wFunc = FO_DELETE;\r
311                         shop.pFrom = buf;\r
312                         shop.fFlags = FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT;\r
313                         if (bTrash)\r
314                                 shop.fFlags |= FOF_ALLOWUNDO;\r
315                         bRet = (SHFileOperation(&shop) == 0);\r
316                         delete [] buf;\r
317                 }\r
318                 else\r
319                 {\r
320                         bRet = !!::DeleteFile(m_sBackslashPath);\r
321                 }\r
322         }\r
323         m_bExists = false;\r
324         m_bExistsKnown = true;\r
325         return bRet;\r
326 }\r
327 \r
328 __int64  CTGitPath::GetLastWriteTime() const\r
329 {\r
330         if(!m_bLastWriteTimeKnown)\r
331         {\r
332                 UpdateAttributes();\r
333         }\r
334         return m_lastWriteTime;\r
335 }\r
336 \r
337 bool CTGitPath::IsReadOnly() const\r
338 {\r
339         if(!m_bLastWriteTimeKnown)\r
340         {\r
341                 UpdateAttributes();\r
342         }\r
343         return m_bIsReadOnly;\r
344 }\r
345 \r
346 void CTGitPath::UpdateAttributes() const\r
347 {\r
348         EnsureBackslashPathSet();\r
349         WIN32_FILE_ATTRIBUTE_DATA attribs;\r
350         if(GetFileAttributesEx(m_sBackslashPath, GetFileExInfoStandard, &attribs))\r
351         {\r
352                 m_bIsDirectory = !!(attribs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
353                 m_lastWriteTime = *(__int64*)&attribs.ftLastWriteTime;\r
354                 m_bIsReadOnly = !!(attribs.dwFileAttributes & FILE_ATTRIBUTE_READONLY);\r
355                 m_bExists = true;\r
356         }\r
357         else\r
358         {\r
359                 DWORD err = GetLastError();\r
360                 if ((err == ERROR_FILE_NOT_FOUND)||(err == ERROR_PATH_NOT_FOUND)||(err == ERROR_INVALID_NAME))\r
361                 {\r
362                         m_bIsDirectory = false;\r
363                         m_lastWriteTime = 0;\r
364                         m_bExists = false;\r
365                 }\r
366                 else\r
367                 {\r
368                         m_bIsDirectory = false;\r
369                         m_lastWriteTime = 0;\r
370                         m_bExists = true;\r
371                         return;\r
372                 }\r
373         }\r
374         m_bDirectoryKnown = true;\r
375         m_bLastWriteTimeKnown = true;\r
376         m_bExistsKnown = true;\r
377 }\r
378 \r
379 \r
380 void CTGitPath::EnsureBackslashPathSet() const\r
381 {\r
382         if(m_sBackslashPath.IsEmpty())\r
383         {\r
384                 SetBackslashPath(m_sFwdslashPath);\r
385                 ATLASSERT(IsEmpty() || !m_sBackslashPath.IsEmpty());\r
386         }\r
387 }\r
388 void CTGitPath::EnsureFwdslashPathSet() const\r
389 {\r
390         if(m_sFwdslashPath.IsEmpty())\r
391         {\r
392                 SetFwdslashPath(m_sBackslashPath);\r
393                 ATLASSERT(IsEmpty() || !m_sFwdslashPath.IsEmpty());\r
394         }\r
395 }\r
396 \r
397 \r
398 // Reset all the caches\r
399 void CTGitPath::Reset()\r
400 {\r
401         m_bDirectoryKnown = false;\r
402         m_bURLKnown = false;\r
403         m_bLastWriteTimeKnown = false;\r
404         m_bHasAdminDirKnown = false;\r
405         m_bIsValidOnWindowsKnown = false;\r
406         m_bIsAdminDirKnown = false;\r
407         m_bExistsKnown = false;\r
408         m_bIsSpecialDirectoryKnown = false;\r
409         m_bIsSpecialDirectory = false;\r
410 \r
411         m_sBackslashPath.Empty();\r
412         m_sFwdslashPath.Empty();\r
413         m_sUTF8FwdslashPath.Empty();\r
414         ATLASSERT(IsEmpty());\r
415 }\r
416 \r
417 CTGitPath CTGitPath::GetDirectory() const\r
418 {\r
419         if ((IsDirectory())||(!Exists()))\r
420         {\r
421                 return *this;\r
422         }\r
423         return GetContainingDirectory();\r
424 }\r
425 \r
426 CTGitPath CTGitPath::GetContainingDirectory() const\r
427 {\r
428         EnsureBackslashPathSet();\r
429 \r
430         CString sDirName = m_sBackslashPath.Left(m_sBackslashPath.ReverseFind('\\'));\r
431         if(sDirName.GetLength() == 2 && sDirName[1] == ':')\r
432         {\r
433                 // This is a root directory, which needs a trailing slash\r
434                 sDirName += '\\';\r
435                 if(sDirName == m_sBackslashPath)\r
436                 {\r
437                         // We were clearly provided with a root path to start with - we should return nothing now\r
438                         sDirName.Empty();\r
439                 }\r
440         }\r
441         if(sDirName.GetLength() == 1 && sDirName[0] == '\\')\r
442         {\r
443                 // We have an UNC path and we already are the root\r
444                 sDirName.Empty();\r
445         }\r
446         CTGitPath retVal;\r
447         retVal.SetFromWin(sDirName);\r
448         return retVal;\r
449 }\r
450 \r
451 CString CTGitPath::GetRootPathString() const\r
452 {\r
453         EnsureBackslashPathSet();\r
454         CString workingPath = m_sBackslashPath;\r
455         LPTSTR pPath = workingPath.GetBuffer(MAX_PATH);         // MAX_PATH ok here.\r
456         ATLVERIFY(::PathStripToRoot(pPath));\r
457         workingPath.ReleaseBuffer();\r
458         return workingPath;\r
459 }\r
460 \r
461 \r
462 CString CTGitPath::GetFilename() const\r
463 {\r
464         ATLASSERT(!IsDirectory());\r
465         return GetFileOrDirectoryName();\r
466 }\r
467 \r
468 CString CTGitPath::GetFileOrDirectoryName() const\r
469 {\r
470         EnsureBackslashPathSet();\r
471         return m_sBackslashPath.Mid(m_sBackslashPath.ReverseFind('\\')+1);\r
472 }\r
473 \r
474 CString CTGitPath::GetUIFileOrDirectoryName() const\r
475 {\r
476         GetUIPathString();\r
477         return m_sUIPath.Mid(m_sUIPath.ReverseFind('\\')+1);\r
478 }\r
479 \r
480 CString CTGitPath::GetFileExtension() const\r
481 {\r
482         if(!IsDirectory())\r
483         {\r
484                 EnsureBackslashPathSet();\r
485                 int dotPos = m_sBackslashPath.ReverseFind('.');\r
486                 int slashPos = m_sBackslashPath.ReverseFind('\\');\r
487                 if (dotPos > slashPos)\r
488                         return m_sBackslashPath.Mid(dotPos);\r
489         }\r
490         return CString();\r
491 }\r
492 \r
493 \r
494 bool CTGitPath::ArePathStringsEqual(const CString& sP1, const CString& sP2)\r
495 {\r
496         int length = sP1.GetLength();\r
497         if(length != sP2.GetLength())\r
498         {\r
499                 // Different lengths\r
500                 return false;\r
501         }\r
502         // We work from the end of the strings, because path differences\r
503         // are more likely to occur at the far end of a string\r
504         LPCTSTR pP1Start = sP1;\r
505         LPCTSTR pP1 = pP1Start+(length-1);\r
506         LPCTSTR pP2 = ((LPCTSTR)sP2)+(length-1);\r
507         while(length-- > 0)\r
508         {\r
509                 if(_totlower(*pP1--) != _totlower(*pP2--))\r
510                 {\r
511                         return false;\r
512                 }\r
513         }\r
514         return true;\r
515 }\r
516 \r
517 bool CTGitPath::ArePathStringsEqualWithCase(const CString& sP1, const CString& sP2)\r
518 {\r
519         int length = sP1.GetLength();\r
520         if(length != sP2.GetLength())\r
521         {\r
522                 // Different lengths\r
523                 return false;\r
524         }\r
525         // We work from the end of the strings, because path differences\r
526         // are more likely to occur at the far end of a string\r
527         LPCTSTR pP1Start = sP1;\r
528         LPCTSTR pP1 = pP1Start+(length-1);\r
529         LPCTSTR pP2 = ((LPCTSTR)sP2)+(length-1);\r
530         while(length-- > 0)\r
531         {\r
532                 if((*pP1--) != (*pP2--))\r
533                 {\r
534                         return false;\r
535                 }\r
536         }\r
537         return true;\r
538 }\r
539 \r
540 bool CTGitPath::IsEmpty() const\r
541 {\r
542         // Check the backward slash path first, since the chance that this\r
543         // one is set is higher. In case of a 'false' return value it's a little\r
544         // bit faster.\r
545         return m_sBackslashPath.IsEmpty() && m_sFwdslashPath.IsEmpty();\r
546 }\r
547 \r
548 // Test if both paths refer to the same item\r
549 // Ignores case and slash direction\r
550 bool CTGitPath::IsEquivalentTo(const CTGitPath& rhs) const\r
551 {\r
552         // Try and find a slash direction which avoids having to convert\r
553         // both filenames\r
554         if(!m_sBackslashPath.IsEmpty())\r
555         {\r
556                 // *We've* got a \ path - make sure that the RHS also has a \ path\r
557                 rhs.EnsureBackslashPathSet();\r
558                 return ArePathStringsEqualWithCase(m_sBackslashPath, rhs.m_sBackslashPath);\r
559         }\r
560         else\r
561         {\r
562                 // Assume we've got a fwdslash path and make sure that the RHS has one\r
563                 rhs.EnsureFwdslashPathSet();\r
564                 return ArePathStringsEqualWithCase(m_sFwdslashPath, rhs.m_sFwdslashPath);\r
565         }\r
566 }\r
567 \r
568 bool CTGitPath::IsEquivalentToWithoutCase(const CTGitPath& rhs) const\r
569 {\r
570         // Try and find a slash direction which avoids having to convert\r
571         // both filenames\r
572         if(!m_sBackslashPath.IsEmpty())\r
573         {\r
574                 // *We've* got a \ path - make sure that the RHS also has a \ path\r
575                 rhs.EnsureBackslashPathSet();\r
576                 return ArePathStringsEqual(m_sBackslashPath, rhs.m_sBackslashPath);\r
577         }\r
578         else\r
579         {\r
580                 // Assume we've got a fwdslash path and make sure that the RHS has one\r
581                 rhs.EnsureFwdslashPathSet();\r
582                 return ArePathStringsEqual(m_sFwdslashPath, rhs.m_sFwdslashPath);\r
583         }\r
584 }\r
585 \r
586 bool CTGitPath::IsAncestorOf(const CTGitPath& possibleDescendant) const\r
587 {\r
588         possibleDescendant.EnsureBackslashPathSet();\r
589         EnsureBackslashPathSet();\r
590 \r
591         bool bPathStringsEqual = ArePathStringsEqual(m_sBackslashPath, possibleDescendant.m_sBackslashPath.Left(m_sBackslashPath.GetLength()));\r
592         if (m_sBackslashPath.GetLength() >= possibleDescendant.GetWinPathString().GetLength())\r
593         {\r
594                 return bPathStringsEqual;               \r
595         }\r
596         \r
597         return (bPathStringsEqual && \r
598                         ((possibleDescendant.m_sBackslashPath[m_sBackslashPath.GetLength()] == '\\')||\r
599                         (m_sBackslashPath.GetLength()==3 && m_sBackslashPath[1]==':')));\r
600 }\r
601 \r
602 // Get a string representing the file path, optionally with a base \r
603 // section stripped off the front.\r
604 CString CTGitPath::GetDisplayString(const CTGitPath* pOptionalBasePath /* = NULL*/) const\r
605 {\r
606         EnsureFwdslashPathSet();\r
607         if(pOptionalBasePath != NULL)\r
608         {\r
609                 // Find the length of the base-path without having to do an 'ensure' on it\r
610                 int baseLength = max(pOptionalBasePath->m_sBackslashPath.GetLength(), pOptionalBasePath->m_sFwdslashPath.GetLength());\r
611 \r
612                 // Now, chop that baseLength of the front of the path\r
613                 return m_sFwdslashPath.Mid(baseLength).TrimLeft('/');\r
614         }\r
615         return m_sFwdslashPath;\r
616 }\r
617 \r
618 int CTGitPath::Compare(const CTGitPath& left, const CTGitPath& right)\r
619 {\r
620         left.EnsureBackslashPathSet();\r
621         right.EnsureBackslashPathSet();\r
622         return left.m_sBackslashPath.CompareNoCase(right.m_sBackslashPath);\r
623 }\r
624 \r
625 bool operator<(const CTGitPath& left, const CTGitPath& right)\r
626 {\r
627         return CTGitPath::Compare(left, right) < 0;\r
628 }\r
629 \r
630 bool CTGitPath::PredLeftEquivalentToRight(const CTGitPath& left, const CTGitPath& right)\r
631 {\r
632         return left.IsEquivalentTo(right);\r
633 }\r
634 \r
635 bool CTGitPath::PredLeftSameWCPathAsRight(const CTGitPath& left, const CTGitPath& right)\r
636 {\r
637         if (left.IsAdminDir() && right.IsAdminDir())\r
638         {\r
639                 CTGitPath l = left;\r
640                 CTGitPath r = right;\r
641                 do \r
642                 {\r
643                         l = l.GetContainingDirectory();\r
644                 } while(l.HasAdminDir());\r
645                 do \r
646                 {\r
647                         r = r.GetContainingDirectory();\r
648                 } while(r.HasAdminDir());\r
649                 return l.GetContainingDirectory().IsEquivalentTo(r.GetContainingDirectory());\r
650         }\r
651         return left.GetDirectory().IsEquivalentTo(right.GetDirectory());\r
652 }\r
653 \r
654 bool CTGitPath::CheckChild(const CTGitPath &parent, const CTGitPath& child)\r
655 {\r
656         return parent.IsAncestorOf(child);\r
657 }\r
658 \r
659 void CTGitPath::AppendRawString(const CString& sAppend)\r
660 {\r
661         EnsureFwdslashPathSet();\r
662         CString strCopy = m_sFwdslashPath += sAppend;\r
663         SetFromUnknown(strCopy);\r
664 }\r
665 \r
666 void CTGitPath::AppendPathString(const CString& sAppend)\r
667 {\r
668         EnsureBackslashPathSet();\r
669         CString cleanAppend(sAppend);\r
670         cleanAppend.Replace('/', '\\');\r
671         cleanAppend.TrimLeft('\\');\r
672         m_sBackslashPath.TrimRight('\\');\r
673         CString strCopy = m_sBackslashPath + _T("\\") + cleanAppend;\r
674         SetFromWin(strCopy);\r
675 }\r
676 \r
677 bool CTGitPath::HasAdminDir() const\r
678 {\r
679         if (m_bHasAdminDirKnown)\r
680                 return m_bHasAdminDir;\r
681 \r
682         EnsureBackslashPathSet();\r
683         m_bHasAdminDir = g_GitAdminDir.HasAdminDir(m_sBackslashPath, IsDirectory());\r
684         m_bHasAdminDirKnown = true;\r
685         return m_bHasAdminDir;\r
686 }\r
687 \r
688 bool CTGitPath::IsAdminDir() const\r
689 {\r
690         if (m_bIsAdminDirKnown)\r
691                 return m_bIsAdminDir;\r
692         \r
693         EnsureBackslashPathSet();\r
694         m_bIsAdminDir = g_GitAdminDir.IsAdminDirPath(m_sBackslashPath);\r
695         m_bIsAdminDirKnown = true;\r
696         return m_bIsAdminDir;\r
697 }\r
698 \r
699 bool CTGitPath::IsValidOnWindows() const\r
700 {\r
701         if (m_bIsValidOnWindowsKnown)\r
702                 return m_bIsValidOnWindows;\r
703 \r
704         m_bIsValidOnWindows = false;\r
705         EnsureBackslashPathSet();\r
706         CString sMatch = m_sBackslashPath + _T("\r\n");\r
707         wstring sPattern;\r
708         // the 'file://' URL is just a normal windows path:\r
709         if (sMatch.Left(7).CompareNoCase(_T("file:\\\\"))==0)\r
710         {\r
711                 sMatch = sMatch.Mid(7);\r
712                 sMatch.TrimLeft(_T("\\"));\r
713                 sPattern = _T("^(\\\\\\\\\\?\\\\)?(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");\r
714         }\r
715         else if (IsUrl())\r
716         {\r
717                 sPattern = _T("^((http|https|svn|svn\\+ssh|file)\\:\\\\+([^\\\\@\\:]+\\:[^\\\\@\\:]+@)?\\\\[^\\\\]+(\\:\\d+)?)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<>\\. ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<>\\. ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");\r
718         }\r
719         else\r
720         {\r
721                 sPattern = _T("^(\\\\\\\\\\?\\\\)?(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");\r
722         }\r
723 \r
724         try\r
725         {\r
726                 tr1::wregex rx(sPattern, tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);\r
727                 tr1::wsmatch match;\r
728 \r
729                 wstring rmatch = wstring((LPCTSTR)sMatch);\r
730                 if (tr1::regex_match(rmatch, match, rx))\r
731                 {\r
732                         if (wstring(match[0]).compare(sMatch)==0)\r
733                                 m_bIsValidOnWindows = true;\r
734                 }\r
735                 if (m_bIsValidOnWindows)\r
736                 {\r
737                         // now check for illegal filenames\r
738                         tr1::wregex rx2(_T("\\\\(lpt\\d|com\\d|aux|nul|prn|con)(\\\\|$)"), tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);\r
739                         rmatch = m_sBackslashPath;\r
740                         if (tr1::regex_search(rmatch, rx2, tr1::regex_constants::match_default))\r
741                                 m_bIsValidOnWindows = false;\r
742                 }\r
743         }\r
744         catch (exception) {}\r
745 \r
746         m_bIsValidOnWindowsKnown = true;\r
747         return m_bIsValidOnWindows;\r
748 }\r
749 \r
750 bool CTGitPath::IsSpecialDirectory() const\r
751 {\r
752         if (m_bIsSpecialDirectoryKnown)\r
753                 return m_bIsSpecialDirectory;\r
754 \r
755         static LPCTSTR specialDirectories[]\r
756                 = { _T("trunk"), _T("tags"), _T("branches") };\r
757 \r
758         for (int i=0 ; i<(sizeof(specialDirectories) / sizeof(specialDirectories[0])) ; ++i)\r
759         {\r
760                 CString name = GetFileOrDirectoryName();\r
761                 if (0 == name.CompareNoCase(specialDirectories[i]))\r
762                 {\r
763                         m_bIsSpecialDirectory = true;\r
764                         break;\r
765                 }\r
766         }\r
767 \r
768         m_bIsSpecialDirectoryKnown = true;\r
769 \r
770         return m_bIsSpecialDirectory;\r
771 }\r
772 \r
773 //////////////////////////////////////////////////////////////////////////\r
774 \r
775 CTGitPathList::CTGitPathList()\r
776 {\r
777 \r
778 }\r
779 \r
780 // A constructor which allows a path list to be easily built which one initial entry in\r
781 CTGitPathList::CTGitPathList(const CTGitPath& firstEntry)\r
782 {\r
783         AddPath(firstEntry);\r
784 }\r
785 \r
786 void CTGitPathList::AddPath(const CTGitPath& newPath)\r
787 {\r
788         m_paths.push_back(newPath);\r
789         m_commonBaseDirectory.Reset();\r
790 }\r
791 int CTGitPathList::GetCount() const\r
792 {\r
793         return (int)m_paths.size();\r
794 }\r
795 void CTGitPathList::Clear()\r
796 {\r
797         m_paths.clear();\r
798         m_commonBaseDirectory.Reset();\r
799 }\r
800 \r
801 const CTGitPath& CTGitPathList::operator[](INT_PTR index) const\r
802 {\r
803         ATLASSERT(index >= 0 && index < (INT_PTR)m_paths.size());\r
804         return m_paths[index];\r
805 }\r
806 \r
807 bool CTGitPathList::AreAllPathsFiles() const\r
808 {\r
809         // Look through the vector for any directories - if we find them, return false\r
810         return std::find_if(m_paths.begin(), m_paths.end(), std::mem_fun_ref(&CTGitPath::IsDirectory)) == m_paths.end();\r
811 }\r
812 \r
813 \r
814 #if defined(_MFC_VER)\r
815 \r
816 bool CTGitPathList::LoadFromFile(const CTGitPath& filename)\r
817 {\r
818         Clear();\r
819         try\r
820         {\r
821                 CString strLine;\r
822                 CStdioFile file(filename.GetWinPath(), CFile::typeBinary | CFile::modeRead | CFile::shareDenyWrite);\r
823 \r
824                 // for every selected file/folder\r
825                 CTGitPath path;\r
826                 while (file.ReadString(strLine))\r
827                 {\r
828                         path.SetFromUnknown(strLine);\r
829                         AddPath(path);\r
830                 }\r
831                 file.Close();\r
832         }\r
833         catch (CFileException* pE)\r
834         {\r
835                 TRACE("CFileException loading target file list\n");\r
836                 TCHAR error[10000] = {0};\r
837                 pE->GetErrorMessage(error, 10000);\r
838 //              CMessageBox::Show(NULL, error, _T("TortoiseGit"), MB_ICONERROR);\r
839                 pE->Delete();\r
840                 return false;\r
841         }\r
842         return true;\r
843 }\r
844 \r
845 bool CTGitPathList::WriteToFile(const CString& sFilename, bool bANSI /* = false */) const\r
846 {\r
847         try\r
848         {\r
849                 if (bANSI)\r
850                 {\r
851                         CStdioFile file(sFilename, CFile::typeText | CFile::modeReadWrite | CFile::modeCreate);\r
852                         PathVector::const_iterator it;\r
853                         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
854                         {\r
855                                 CStringA line = CStringA(it->GetGitPathString()) + '\n';\r
856                                 file.Write(line, line.GetLength());\r
857                         } \r
858                         file.Close();\r
859                 }\r
860                 else\r
861                 {\r
862                         CStdioFile file(sFilename, CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate);\r
863                         PathVector::const_iterator it;\r
864                         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
865                         {\r
866                                 file.WriteString(it->GetGitPathString()+_T("\n"));\r
867                         } \r
868                         file.Close();\r
869                 }\r
870         }\r
871         catch (CFileException* pE)\r
872         {\r
873                 TRACE("CFileException in writing temp file\n");\r
874                 pE->Delete();\r
875                 return false;\r
876         }\r
877         return true;\r
878 }\r
879 \r
880 \r
881 void CTGitPathList::LoadFromAsteriskSeparatedString(const CString& sPathString)\r
882 {\r
883         int pos = 0;\r
884         CString temp;\r
885         for(;;)\r
886         {\r
887                 temp = sPathString.Tokenize(_T("*"),pos);\r
888                 if(temp.IsEmpty())\r
889                 {\r
890                         break;\r
891                 }\r
892                 AddPath(CTGitPath(CPathUtils::GetLongPathname(temp)));\r
893         } \r
894 }\r
895 \r
896 CString CTGitPathList::CreateAsteriskSeparatedString() const\r
897 {\r
898         CString sRet;\r
899         PathVector::const_iterator it;\r
900         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
901         {\r
902                 if (!sRet.IsEmpty())\r
903                         sRet += _T("*");\r
904                 sRet += it->GetWinPathString();\r
905         }\r
906         return sRet;\r
907 }\r
908 #endif // _MFC_VER\r
909 \r
910 bool \r
911 CTGitPathList::AreAllPathsFilesInOneDirectory() const\r
912 {\r
913         // Check if all the paths are files and in the same directory\r
914         PathVector::const_iterator it;\r
915         m_commonBaseDirectory.Reset();\r
916         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
917         {\r
918                 if(it->IsDirectory())\r
919                 {\r
920                         return false;\r
921                 }\r
922                 const CTGitPath& baseDirectory = it->GetDirectory();\r
923                 if(m_commonBaseDirectory.IsEmpty())\r
924                 {\r
925                         m_commonBaseDirectory = baseDirectory;\r
926                 }\r
927                 else if(!m_commonBaseDirectory.IsEquivalentTo(baseDirectory))\r
928                 {\r
929                         // Different path\r
930                         m_commonBaseDirectory.Reset();\r
931                         return false;\r
932                 }\r
933         }\r
934         return true;\r
935 }\r
936 \r
937 CTGitPath CTGitPathList::GetCommonDirectory() const\r
938 {\r
939         if (m_commonBaseDirectory.IsEmpty())\r
940         {\r
941                 PathVector::const_iterator it;\r
942                 for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
943                 {\r
944                         const CTGitPath& baseDirectory = it->GetDirectory();\r
945                         if(m_commonBaseDirectory.IsEmpty())\r
946                         {\r
947                                 m_commonBaseDirectory = baseDirectory;\r
948                         }\r
949                         else if(!m_commonBaseDirectory.IsEquivalentTo(baseDirectory))\r
950                         {\r
951                                 // Different path\r
952                                 m_commonBaseDirectory.Reset();\r
953                                 break;\r
954                         }\r
955                 }\r
956         }\r
957         // since we only checked strings, not paths,\r
958         // we have to make sure now that we really return a *path* here\r
959         PathVector::const_iterator iter;\r
960         for(iter = m_paths.begin(); iter != m_paths.end(); ++iter)\r
961         {\r
962                 if (!m_commonBaseDirectory.IsAncestorOf(*iter))\r
963                 {\r
964                         m_commonBaseDirectory = m_commonBaseDirectory.GetContainingDirectory();\r
965                         break;\r
966                 }\r
967         }       \r
968         return m_commonBaseDirectory;\r
969 }\r
970 \r
971 CTGitPath CTGitPathList::GetCommonRoot() const\r
972 {\r
973         PathVector::const_iterator it;\r
974         CString sRoot, sTempRoot;\r
975         bool bEqual = true;\r
976 \r
977         if (GetCount() == 1)\r
978                 return m_paths[0];\r
979 \r
980         int backSlashPos = 0;\r
981         int searchStartPos = 0;\r
982         while (bEqual)\r
983         {\r
984                 for (it = m_paths.begin(); it != m_paths.end(); ++it)\r
985                 {\r
986                         if (backSlashPos == 0)\r
987                         {\r
988                                 backSlashPos = it->GetWinPathString().Find('\\', searchStartPos+1);\r
989                                 if ((backSlashPos < 0)&&(searchStartPos != it->GetWinPathString().GetLength()))\r
990                                         backSlashPos = it->GetWinPathString().GetLength();\r
991                         }\r
992                         else if (it->GetWinPathString().Find('\\', searchStartPos+1) != backSlashPos)\r
993                         {\r
994                                 if (it->GetWinPathString().Find('\\', searchStartPos+1) < 0)\r
995                                 {\r
996                                         if (it->GetWinPathString().GetLength() != backSlashPos)\r
997                                         {\r
998                                                 bEqual = false;\r
999                                                 break;\r
1000                                         }\r
1001                                 }\r
1002                                 else\r
1003                                 {\r
1004                                         bEqual = false;\r
1005                                         break;\r
1006                                 }\r
1007                         }\r
1008                         if (backSlashPos < 0)\r
1009                         {\r
1010                                 bEqual = false;\r
1011                                 break;\r
1012                         }\r
1013                 }\r
1014                 if (bEqual == false)\r
1015                 {\r
1016                         if (searchStartPos)\r
1017                                 sRoot = m_paths[0].GetWinPathString().Left(searchStartPos+1);\r
1018                 }\r
1019                 else\r
1020                 {\r
1021                         searchStartPos = backSlashPos;\r
1022                 }\r
1023                 backSlashPos = 0;\r
1024         }\r
1025 \r
1026         return CTGitPath(sRoot.TrimRight('\\'));\r
1027 }\r
1028 \r
1029 void CTGitPathList::SortByPathname(bool bReverse /*= false*/)\r
1030 {\r
1031         std::sort(m_paths.begin(), m_paths.end());\r
1032         if (bReverse)\r
1033                 std::reverse(m_paths.begin(), m_paths.end());\r
1034 }\r
1035 \r
1036 void CTGitPathList::DeleteAllFiles(bool bTrash)\r
1037 {\r
1038         PathVector::const_iterator it;\r
1039         if (bTrash)\r
1040         {\r
1041                 SortByPathname();\r
1042                 CString sPaths;\r
1043                 for (it = m_paths.begin(); it != m_paths.end(); ++it)\r
1044                 {\r
1045                         if ((it->Exists())&&(!it->IsDirectory()))\r
1046                         {\r
1047                                 ::SetFileAttributes(it->GetWinPath(), FILE_ATTRIBUTE_NORMAL);\r
1048                                 sPaths += it->GetWinPath();\r
1049                                 sPaths += '\0';\r
1050                         }\r
1051                 }\r
1052                 sPaths += '\0';\r
1053                 sPaths += '\0';\r
1054                 SHFILEOPSTRUCT shop = {0};\r
1055                 shop.wFunc = FO_DELETE;\r
1056                 shop.pFrom = (LPCTSTR)sPaths;\r
1057                 shop.fFlags = FOF_ALLOWUNDO|FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT;\r
1058                 SHFileOperation(&shop);\r
1059         }\r
1060         else\r
1061         {\r
1062                 for (it = m_paths.begin(); it != m_paths.end(); ++it)\r
1063                 {\r
1064                         if (!it->IsDirectory())\r
1065                         {\r
1066                                 ::SetFileAttributes(it->GetWinPath(), FILE_ATTRIBUTE_NORMAL);\r
1067                                 ::DeleteFile(it->GetWinPath());\r
1068                         }\r
1069                 }\r
1070         }\r
1071         Clear();\r
1072 }\r
1073 \r
1074 void CTGitPathList::RemoveDuplicates()\r
1075 {\r
1076         SortByPathname();\r
1077         // Remove the duplicates\r
1078         // (Unique moves them to the end of the vector, then erase chops them off)\r
1079         m_paths.erase(std::unique(m_paths.begin(), m_paths.end(), &CTGitPath::PredLeftEquivalentToRight), m_paths.end());\r
1080 }\r
1081 \r
1082 void CTGitPathList::RemoveAdminPaths()\r
1083 {\r
1084         PathVector::iterator it;\r
1085         for(it = m_paths.begin(); it != m_paths.end(); )\r
1086         {\r
1087                 if (it->IsAdminDir())\r
1088                 {\r
1089                         m_paths.erase(it);\r
1090                         it = m_paths.begin();\r
1091                 }\r
1092                 else\r
1093                         ++it;\r
1094         }\r
1095 }\r
1096 \r
1097 void CTGitPathList::RemovePath(const CTGitPath& path)\r
1098 {\r
1099         PathVector::iterator it;\r
1100         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
1101         {\r
1102                 if (it->IsEquivalentTo(path))\r
1103                 {\r
1104                         m_paths.erase(it);\r
1105                         return;\r
1106                 }\r
1107         }\r
1108 }\r
1109 \r
1110 void CTGitPathList::RemoveChildren()\r
1111 {\r
1112         SortByPathname();\r
1113         m_paths.erase(std::unique(m_paths.begin(), m_paths.end(), &CTGitPath::CheckChild), m_paths.end());\r
1114 }\r
1115 \r
1116 bool CTGitPathList::IsEqual(const CTGitPathList& list)\r
1117 {\r
1118         if (list.GetCount() != GetCount())\r
1119                 return false;\r
1120         for (int i=0; i<list.GetCount(); ++i)\r
1121         {\r
1122                 if (!list[i].IsEquivalentTo(m_paths[i]))\r
1123                         return false;\r
1124         }\r
1125         return true;\r
1126 }\r
1127 \r
1128 //////////////////////////////////////////////////////////////////////////\r
1129 #if 0\r
1130 apr_array_header_t * CTGitPathList::MakePathArray (apr_pool_t *pool) const\r
1131 {\r
1132         apr_array_header_t *targets = apr_array_make (pool, GetCount(), sizeof(const char *));\r
1133 \r
1134         for(int nItem = 0; nItem < GetCount(); nItem++)\r
1135         {\r
1136                 const char * target = m_paths[nItem].GetGitApiPath(pool);\r
1137                 (*((const char **) apr_array_push (targets))) = target;\r
1138         }\r
1139 \r
1140         return targets;\r
1141 }\r
1142 #endif\r
1143 //////////////////////////////////////////////////////////////////////////\r
1144 \r
1145 #if 0\r
1146 #if defined(_DEBUG)\r
1147 // Some test cases for these classes\r
1148 static class CTGitPathTests\r
1149 {\r
1150 public:\r
1151         CTGitPathTests()\r
1152         {\r
1153                 apr_initialize();\r
1154                 pool = svn_pool_create(NULL);\r
1155                 GetDirectoryTest();\r
1156                 AdminDirTest();\r
1157                 SortTest();\r
1158                 RawAppendTest();\r
1159                 PathAppendTest();\r
1160                 RemoveDuplicatesTest();\r
1161                 RemoveChildrenTest();\r
1162                 ContainingDirectoryTest();\r
1163                 AncestorTest();\r
1164                 SubversionPathTest();\r
1165                 GetCommonRootTest();\r
1166 #if defined(_MFC_VER)\r
1167                 ValidPathAndUrlTest();\r
1168                 ListLoadingTest();\r
1169 #endif\r
1170                 apr_terminate();\r
1171         }\r
1172 \r
1173 private:\r
1174 //      apr_pool_t * pool;\r
1175         void GetDirectoryTest()\r
1176         {\r
1177                 // Bit tricky, this test, because we need to know something about the file\r
1178                 // layout on the machine which is running the test\r
1179                 TCHAR winDir[MAX_PATH+1];\r
1180                 GetWindowsDirectory(winDir, MAX_PATH);\r
1181                 CString sWinDir(winDir);\r
1182 \r
1183                 CTGitPath testPath;\r
1184                 // This is a file which we know will always be there\r
1185                 testPath.SetFromUnknown(sWinDir + _T("\\win.ini"));\r
1186                 ATLASSERT(!testPath.IsDirectory());\r
1187                 ATLASSERT(testPath.GetDirectory().GetWinPathString() == sWinDir);\r
1188                 ATLASSERT(testPath.GetContainingDirectory().GetWinPathString() == sWinDir);\r
1189 \r
1190                 // Now do the test on the win directory itself - It's hard to be sure about the containing directory\r
1191                 // but we know it must be different to the directory itself\r
1192                 testPath.SetFromUnknown(sWinDir);\r
1193                 ATLASSERT(testPath.IsDirectory());\r
1194                 ATLASSERT(testPath.GetDirectory().GetWinPathString() == sWinDir);\r
1195                 ATLASSERT(testPath.GetContainingDirectory().GetWinPathString() != sWinDir);\r
1196                 ATLASSERT(testPath.GetContainingDirectory().GetWinPathString().GetLength() < sWinDir.GetLength());\r
1197 \r
1198                 // Try a root path\r
1199                 testPath.SetFromUnknown(_T("C:\\"));\r
1200                 ATLASSERT(testPath.IsDirectory());\r
1201                 ATLASSERT(testPath.GetDirectory().GetWinPathString().CompareNoCase(_T("C:\\"))==0);\r
1202                 ATLASSERT(testPath.GetContainingDirectory().IsEmpty());\r
1203                 // Try a root UNC path\r
1204                 testPath.SetFromUnknown(_T("\\MYSTATION"));\r
1205                 ATLASSERT(testPath.GetContainingDirectory().IsEmpty());\r
1206         }\r
1207 \r
1208         void AdminDirTest()\r
1209         {\r
1210                 CTGitPath testPath;\r
1211                 testPath.SetFromUnknown(_T("c:\\.svndir"));\r
1212                 ATLASSERT(!testPath.IsAdminDir());\r
1213                 testPath.SetFromUnknown(_T("c:\\test.svn"));\r
1214                 ATLASSERT(!testPath.IsAdminDir());\r
1215                 testPath.SetFromUnknown(_T("c:\\.svn"));\r
1216                 ATLASSERT(testPath.IsAdminDir());\r
1217                 testPath.SetFromUnknown(_T("c:\\.svndir\\test"));\r
1218                 ATLASSERT(!testPath.IsAdminDir());\r
1219                 testPath.SetFromUnknown(_T("c:\\.svn\\test"));\r
1220                 ATLASSERT(testPath.IsAdminDir());\r
1221                 \r
1222                 CTGitPathList pathList;\r
1223                 pathList.AddPath(CTGitPath(_T("c:\\.svndir")));\r
1224                 pathList.AddPath(CTGitPath(_T("c:\\.svn")));\r
1225                 pathList.AddPath(CTGitPath(_T("c:\\.svn\\test")));\r
1226                 pathList.AddPath(CTGitPath(_T("c:\\test")));\r
1227                 pathList.RemoveAdminPaths();\r
1228                 ATLASSERT(pathList.GetCount()==2);\r
1229                 pathList.Clear();\r
1230                 pathList.AddPath(CTGitPath(_T("c:\\test")));\r
1231                 pathList.RemoveAdminPaths();\r
1232                 ATLASSERT(pathList.GetCount()==1);\r
1233         }\r
1234         \r
1235         void SortTest()\r
1236         {\r
1237                 CTGitPathList testList;\r
1238                 CTGitPath testPath;\r
1239                 testPath.SetFromUnknown(_T("c:/Z"));\r
1240                 testList.AddPath(testPath);\r
1241                 testPath.SetFromUnknown(_T("c:/B"));\r
1242                 testList.AddPath(testPath);\r
1243                 testPath.SetFromUnknown(_T("c:\\a"));\r
1244                 testList.AddPath(testPath);\r
1245                 testPath.SetFromUnknown(_T("c:/Test"));\r
1246                 testList.AddPath(testPath);\r
1247 \r
1248                 testList.SortByPathname();\r
1249 \r
1250                 ATLASSERT(testList[0].GetWinPathString() == _T("c:\\a"));\r
1251                 ATLASSERT(testList[1].GetWinPathString() == _T("c:\\B"));\r
1252                 ATLASSERT(testList[2].GetWinPathString() == _T("c:\\Test"));\r
1253                 ATLASSERT(testList[3].GetWinPathString() == _T("c:\\Z"));\r
1254         }\r
1255 \r
1256         void RawAppendTest()\r
1257         {\r
1258                 CTGitPath testPath(_T("c:/test/"));\r
1259                 testPath.AppendRawString(_T("/Hello"));\r
1260                 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello"));\r
1261 \r
1262                 testPath.AppendRawString(_T("\\T2"));\r
1263                 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello\\T2"));\r
1264 \r
1265                 CTGitPath testFilePath(_T("C:\\windows\\win.ini"));\r
1266                 CTGitPath testBasePath(_T("c:/temp/myfile.txt"));\r
1267                 testBasePath.AppendRawString(testFilePath.GetFileExtension());\r
1268                 ATLASSERT(testBasePath.GetWinPathString() == _T("c:\\temp\\myfile.txt.ini"));\r
1269         }\r
1270 \r
1271         void PathAppendTest()\r
1272         {\r
1273                 CTGitPath testPath(_T("c:/test/"));\r
1274                 testPath.AppendPathString(_T("/Hello"));\r
1275                 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello"));\r
1276 \r
1277                 testPath.AppendPathString(_T("T2"));\r
1278                 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello\\T2"));\r
1279 \r
1280                 CTGitPath testFilePath(_T("C:\\windows\\win.ini"));\r
1281                 CTGitPath testBasePath(_T("c:/temp/myfile.txt"));\r
1282                 // You wouldn't want to do this in real life - you'd use append-raw\r
1283                 testBasePath.AppendPathString(testFilePath.GetFileExtension());\r
1284                 ATLASSERT(testBasePath.GetWinPathString() == _T("c:\\temp\\myfile.txt\\.ini"));\r
1285         }\r
1286 \r
1287         void RemoveDuplicatesTest()\r
1288         {\r
1289                 CTGitPathList list;\r
1290                 list.AddPath(CTGitPath(_T("Z")));\r
1291                 list.AddPath(CTGitPath(_T("A")));\r
1292                 list.AddPath(CTGitPath(_T("E")));\r
1293                 list.AddPath(CTGitPath(_T("E")));\r
1294 \r
1295                 ATLASSERT(list[2].IsEquivalentTo(list[3]));\r
1296                 ATLASSERT(list[2]==list[3]);\r
1297                 \r
1298                 ATLASSERT(list.GetCount() == 4);\r
1299 \r
1300                 list.RemoveDuplicates();\r
1301 \r
1302                 ATLASSERT(list.GetCount() == 3);\r
1303 \r
1304                 ATLASSERT(list[0].GetWinPathString() == _T("A"));\r
1305                 ATLASSERT(list[1].GetWinPathString().Compare(_T("E")) == 0);\r
1306                 ATLASSERT(list[2].GetWinPathString() == _T("Z"));\r
1307         }\r
1308         \r
1309         void RemoveChildrenTest()\r
1310         {\r
1311                 CTGitPathList list;\r
1312                 list.AddPath(CTGitPath(_T("c:\\test")));\r
1313                 list.AddPath(CTGitPath(_T("c:\\test\\file")));\r
1314                 list.AddPath(CTGitPath(_T("c:\\testfile")));\r
1315                 list.AddPath(CTGitPath(_T("c:\\parent")));\r
1316                 list.AddPath(CTGitPath(_T("c:\\parent\\child")));\r
1317                 list.AddPath(CTGitPath(_T("c:\\parent\\child1")));\r
1318                 list.AddPath(CTGitPath(_T("c:\\parent\\child2")));\r
1319                 \r
1320                 ATLASSERT(list.GetCount() == 7);\r
1321 \r
1322                 list.RemoveChildren();\r
1323                 \r
1324                 ATLTRACE("count = %d\n", list.GetCount());\r
1325                 ATLASSERT(list.GetCount() == 3);\r
1326 \r
1327                 list.SortByPathname();\r
1328 \r
1329                 ATLASSERT(list[0].GetWinPathString().Compare(_T("c:\\parent")) == 0);\r
1330                 ATLASSERT(list[1].GetWinPathString().Compare(_T("c:\\test")) == 0);\r
1331                 ATLASSERT(list[2].GetWinPathString().Compare(_T("c:\\testfile")) == 0);\r
1332         }\r
1333 \r
1334 #if defined(_MFC_VER)\r
1335         void ListLoadingTest()\r
1336         {\r
1337                 TCHAR buf[MAX_PATH];\r
1338                 GetCurrentDirectory(MAX_PATH, buf);\r
1339                 CString sPathList(_T("Path1*c:\\path2 with spaces and stuff*\\funnypath\\*"));\r
1340                 CTGitPathList testList;\r
1341                 testList.LoadFromAsteriskSeparatedString(sPathList);\r
1342 \r
1343                 ATLASSERT(testList.GetCount() == 3);\r
1344                 ATLASSERT(testList[0].GetWinPathString() == CString(buf) + _T("\\Path1"));\r
1345                 ATLASSERT(testList[1].GetWinPathString() == _T("c:\\path2 with spaces and stuff"));\r
1346                 ATLASSERT(testList[2].GetWinPathString() == _T("\\funnypath"));\r
1347                 \r
1348                 ATLASSERT(testList.GetCommonRoot().GetWinPathString() == _T(""));\r
1349                 testList.Clear();\r
1350                 sPathList = _T("c:\\path2 with spaces and stuff*c:\\funnypath\\*");\r
1351                 testList.LoadFromAsteriskSeparatedString(sPathList);\r
1352                 ATLASSERT(testList.GetCommonRoot().GetWinPathString() == _T("c:\\"));\r
1353         }\r
1354 #endif \r
1355 \r
1356         void ContainingDirectoryTest()\r
1357         {\r
1358 \r
1359                 CTGitPath testPath;\r
1360                 testPath.SetFromWin(_T("c:\\a\\b\\c\\d\\e"));\r
1361                 CTGitPath dir;\r
1362                 dir = testPath.GetContainingDirectory();\r
1363                 ATLASSERT(dir.GetWinPathString() == _T("c:\\a\\b\\c\\d"));\r
1364                 dir = dir.GetContainingDirectory();\r
1365                 ATLASSERT(dir.GetWinPathString() == _T("c:\\a\\b\\c"));\r
1366                 dir = dir.GetContainingDirectory();\r
1367                 ATLASSERT(dir.GetWinPathString() == _T("c:\\a\\b"));\r
1368                 dir = dir.GetContainingDirectory();\r
1369                 ATLASSERT(dir.GetWinPathString() == _T("c:\\a"));\r
1370                 dir = dir.GetContainingDirectory();\r
1371                 ATLASSERT(dir.GetWinPathString() == _T("c:\\"));\r
1372                 dir = dir.GetContainingDirectory();\r
1373                 ATLASSERT(dir.IsEmpty());\r
1374                 ATLASSERT(dir.GetWinPathString() == _T(""));\r
1375         }\r
1376         \r
1377         void AncestorTest()\r
1378         {\r
1379                 CTGitPath testPath;\r
1380                 testPath.SetFromWin(_T("c:\\windows"));\r
1381                 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\")))==false);\r
1382                 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windows"))));\r
1383                 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windowsdummy")))==false);\r
1384                 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windows\\test.txt"))));\r
1385                 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windows\\system32\\test.txt"))));\r
1386         }\r
1387 \r
1388         void SubversionPathTest()\r
1389         {\r
1390                 CTGitPath testPath;\r
1391                 testPath.SetFromWin(_T("c:\\"));\r
1392                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "c:") == 0);\r
1393                 testPath.SetFromWin(_T("c:\\folder"));\r
1394                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "c:/folder") == 0);\r
1395                 testPath.SetFromWin(_T("c:\\a\\b\\c\\d\\e"));\r
1396                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "c:/a/b/c/d/e") == 0);\r
1397                 testPath.SetFromUnknown(_T("http://testing/"));\r
1398                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing") == 0);\r
1399                 testPath.SetFromGit(NULL);\r
1400                 ATLASSERT(strlen(testPath.GetGitApiPath(pool))==0);\r
1401 #if defined(_MFC_VER)\r
1402                 testPath.SetFromUnknown(_T("http://testing again"));\r
1403                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing%20again") == 0);\r
1404                 testPath.SetFromUnknown(_T("http://testing%20again"));\r
1405                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing%20again") == 0);\r
1406                 testPath.SetFromUnknown(_T("http://testing special chars \344\366\374"));\r
1407                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing%20special%20chars%20%c3%a4%c3%b6%c3%bc") == 0);          \r
1408 #endif\r
1409         }\r
1410 \r
1411         void GetCommonRootTest()\r
1412         {\r
1413                 CTGitPath pathA (_T("C:\\Development\\LogDlg.cpp"));\r
1414                 CTGitPath pathB (_T("C:\\Development\\LogDlg.h"));\r
1415                 CTGitPath pathC (_T("C:\\Development\\SomeDir\\LogDlg.h"));\r
1416                 \r
1417                 CTGitPathList list;\r
1418                 list.AddPath(pathA);\r
1419                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development\\LogDlg.cpp"))==0);\r
1420                 list.AddPath(pathB);\r
1421                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development"))==0);\r
1422                 list.AddPath(pathC);\r
1423                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development"))==0);\r
1424 #ifdef _MFC_VER\r
1425                 list.Clear();\r
1426                 CString sPathList = _T("D:\\Development\\StExBar\\StExBar\\src\\setup\\Setup64.wxs*D:\\Development\\StExBar\\StExBar\\src\\setup\\Setup.wxs*D:\\Development\\StExBar\\SKTimeStamp\\src\\setup\\Setup.wxs*D:\\Development\\StExBar\\SKTimeStamp\\src\\setup\\Setup64.wxs");\r
1427                 list.LoadFromAsteriskSeparatedString(sPathList);\r
1428                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("D:\\Development\\StExBar"))==0);\r
1429 \r
1430                 list.Clear();\r
1431                 sPathList = _T("c:\\windows\\explorer.exe*c:\\windows");\r
1432                 list.LoadFromAsteriskSeparatedString(sPathList);\r
1433                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);\r
1434 \r
1435                 list.Clear();\r
1436                 sPathList = _T("c:\\windows\\*c:\\windows");\r
1437                 list.LoadFromAsteriskSeparatedString(sPathList);\r
1438                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);\r
1439 \r
1440                 list.Clear();\r
1441                 sPathList = _T("c:\\windows\\system32*c:\\windows\\system");\r
1442                 list.LoadFromAsteriskSeparatedString(sPathList);\r
1443                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);\r
1444 \r
1445                 list.Clear();\r
1446                 sPathList = _T("c:\\windowsdummy*c:\\windows");\r
1447                 list.LoadFromAsteriskSeparatedString(sPathList);\r
1448                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\"))==0);\r
1449 #endif\r
1450         }\r
1451         \r
1452         void ValidPathAndUrlTest()\r
1453         {\r
1454                 CTGitPath testPath;\r
1455                 testPath.SetFromWin(_T("c:\\a\\b\\c.test.txt"));\r
1456                 ATLASSERT(testPath.IsValidOnWindows());\r
1457                 testPath.SetFromWin(_T("c:\\"));\r
1458                 ATLASSERT(testPath.IsValidOnWindows());\r
1459                 testPath.SetFromWin(_T("D:\\.Net\\SpindleSearch\\"));\r
1460                 ATLASSERT(testPath.IsValidOnWindows());\r
1461                 testPath.SetFromWin(_T("c"));\r
1462                 ATLASSERT(testPath.IsValidOnWindows());\r
1463                 testPath.SetFromWin(_T("c:\\test folder\\file"));\r
1464                 ATLASSERT(testPath.IsValidOnWindows());\r
1465                 testPath.SetFromWin(_T("c:\\folder\\"));\r
1466                 ATLASSERT(testPath.IsValidOnWindows());\r
1467                 testPath.SetFromWin(_T("c:\\ext.ext.ext\\ext.ext.ext.ext"));\r
1468                 ATLASSERT(testPath.IsValidOnWindows());\r
1469                 testPath.SetFromWin(_T("c:\\.svn"));\r
1470                 ATLASSERT(testPath.IsValidOnWindows());\r
1471                 testPath.SetFromWin(_T("c:\\com\\file"));\r
1472                 ATLASSERT(testPath.IsValidOnWindows());\r
1473                 testPath.SetFromWin(_T("c:\\test\\conf"));\r
1474                 ATLASSERT(testPath.IsValidOnWindows());\r
1475                 testPath.SetFromWin(_T("c:\\LPT"));\r
1476                 ATLASSERT(testPath.IsValidOnWindows());\r
1477                 testPath.SetFromWin(_T("c:\\test\\LPT"));\r
1478                 ATLASSERT(testPath.IsValidOnWindows());\r
1479                 testPath.SetFromWin(_T("c:\\com1test"));\r
1480                 ATLASSERT(testPath.IsValidOnWindows());\r
1481                 testPath.SetFromWin(_T("\\\\?\\c:\\test\\com1test"));\r
1482                 ATLASSERT(testPath.IsValidOnWindows());\r
1483 \r
1484                 testPath.SetFromWin(_T("\\\\Share\\filename"));\r
1485                 ATLASSERT(testPath.IsValidOnWindows());\r
1486                 testPath.SetFromWin(_T("\\\\Share\\filename.extension"));\r
1487                 ATLASSERT(testPath.IsValidOnWindows());\r
1488                 testPath.SetFromWin(_T("\\\\Share\\.svn"));\r
1489                 ATLASSERT(testPath.IsValidOnWindows());\r
1490 \r
1491                 // now the negative tests\r
1492                 testPath.SetFromWin(_T("c:\\test:folder"));\r
1493                 ATLASSERT(!testPath.IsValidOnWindows());\r
1494                 testPath.SetFromWin(_T("c:\\file<name"));\r
1495                 ATLASSERT(!testPath.IsValidOnWindows());\r
1496                 testPath.SetFromWin(_T("c:\\something*else"));\r
1497                 ATLASSERT(!testPath.IsValidOnWindows());\r
1498                 testPath.SetFromWin(_T("c:\\folder\\file?nofile"));\r
1499                 ATLASSERT(!testPath.IsValidOnWindows());\r
1500                 testPath.SetFromWin(_T("c:\\ext.>ension"));\r
1501                 ATLASSERT(!testPath.IsValidOnWindows());\r
1502                 testPath.SetFromWin(_T("c:\\com1\\filename"));\r
1503                 ATLASSERT(!testPath.IsValidOnWindows());\r
1504                 testPath.SetFromWin(_T("c:\\com1"));\r
1505                 ATLASSERT(!testPath.IsValidOnWindows());\r
1506                 testPath.SetFromWin(_T("c:\\com1\\AuX"));\r
1507                 ATLASSERT(!testPath.IsValidOnWindows());\r
1508 \r
1509                 testPath.SetFromWin(_T("\\\\Share\\lpt9\\filename"));\r
1510                 ATLASSERT(!testPath.IsValidOnWindows());\r
1511                 testPath.SetFromWin(_T("\\\\Share\\prn"));\r
1512                 ATLASSERT(!testPath.IsValidOnWindows());\r
1513                 testPath.SetFromWin(_T("\\\\Share\\NUL"));\r
1514                 ATLASSERT(!testPath.IsValidOnWindows());\r
1515                 \r
1516                 // now come some URL tests\r
1517                 testPath.SetFromGit(_T("http://myserver.com/repos/trunk"));\r
1518                 ATLASSERT(testPath.IsValidOnWindows());\r
1519                 testPath.SetFromGit(_T("https://myserver.com/repos/trunk/file%20with%20spaces"));\r
1520                 ATLASSERT(testPath.IsValidOnWindows());\r
1521                 testPath.SetFromGit(_T("svn://myserver.com/repos/trunk/file with spaces"));\r
1522                 ATLASSERT(testPath.IsValidOnWindows());\r
1523                 testPath.SetFromGit(_T("svn+ssh://www.myserver.com/repos/trunk"));\r
1524                 ATLASSERT(testPath.IsValidOnWindows());\r
1525                 testPath.SetFromGit(_T("http://localhost:90/repos/trunk"));\r
1526                 ATLASSERT(testPath.IsValidOnWindows());\r
1527                 testPath.SetFromGit(_T("file:///C:/GitRepos/Tester/Proj1/tags/t2"));\r
1528                 ATLASSERT(testPath.IsValidOnWindows());\r
1529                 // and some negative URL tests\r
1530                 testPath.SetFromGit(_T("httpp://myserver.com/repos/trunk"));\r
1531                 ATLASSERT(!testPath.IsValidOnWindows());\r
1532                 testPath.SetFromGit(_T("https://myserver.com/rep:os/trunk/file%20with%20spaces"));\r
1533                 ATLASSERT(!testPath.IsValidOnWindows());\r
1534                 testPath.SetFromGit(_T("svn://myserver.com/rep<os/trunk/file with spaces"));\r
1535                 ATLASSERT(!testPath.IsValidOnWindows());\r
1536                 testPath.SetFromGit(_T("svn+ssh://www.myserver.com/repos/trunk/prn/"));\r
1537                 ATLASSERT(!testPath.IsValidOnWindows());\r
1538                 testPath.SetFromGit(_T("http://localhost:90/repos/trunk/com1"));\r
1539                 ATLASSERT(!testPath.IsValidOnWindows());\r
1540                 \r
1541         }\r
1542 \r
1543 } TGitPathTestobject;\r
1544 #endif\r
1545 #endif\r
1546 \r