1 // TortoiseSVN - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2011 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "UnicodeUtils.h"
21 #include "stringutils.h"
22 #include "ClipboardHelper.h"
23 #include "SmartHandle.h"
25 int strwildcmp(const char *wild
, const char *string
)
27 const char *cp
= NULL
;
28 const char *mp
= NULL
;
29 while ((*string
) && (*wild
!= '*'))
31 if ((*wild
!= *string
) && (*wild
!= '?'))
49 else if ((*wild
== *string
) || (*wild
== '?'))
68 int wcswildcmp(const wchar_t *wild
, const wchar_t *string
)
70 const wchar_t *cp
= NULL
;
71 const wchar_t *mp
= NULL
;
72 while ((*string
) && (*wild
!= '*'))
74 if ((*wild
!= *string
) && (*wild
!= '?'))
92 else if ((*wild
== *string
) || (*wild
== '?'))
113 void CStringUtils::RemoveAccelerators(CString
& text
)
116 while ((pos
=text
.Find('&',pos
))>=0)
118 if (text
.GetLength() > (pos
-1))
120 if (text
.GetAt(pos
+1)!=' ')
127 bool CStringUtils::WriteAsciiStringToClipboard(const CStringA
& sClipdata
, LCID lcid
, HWND hOwningWnd
)
129 CClipboardHelper clipboardHelper
;
130 if (clipboardHelper
.Open(hOwningWnd
))
133 HGLOBAL hClipboardData
= CClipboardHelper::GlobalAlloc(sClipdata
.GetLength()+1);
136 char* pchData
= (char*)GlobalLock(hClipboardData
);
139 strcpy_s(pchData
, sClipdata
.GetLength()+1, (LPCSTR
)sClipdata
);
140 GlobalUnlock(hClipboardData
);
141 if (SetClipboardData(CF_TEXT
, hClipboardData
))
143 HANDLE hlocmem
= CClipboardHelper::GlobalAlloc(sizeof(LCID
));
146 PLCID plcid
= (PLCID
)GlobalLock(hlocmem
);
150 SetClipboardData(CF_LOCALE
, static_cast<HANDLE
>(plcid
));
152 GlobalUnlock(hlocmem
);
162 bool CStringUtils::WriteAsciiStringToClipboard(const CStringW
& sClipdata
, HWND hOwningWnd
)
164 CClipboardHelper clipboardHelper
;
165 if (clipboardHelper
.Open(hOwningWnd
))
168 HGLOBAL hClipboardData
= CClipboardHelper::GlobalAlloc((sClipdata
.GetLength()+1)*sizeof(WCHAR
));
171 WCHAR
* pchData
= (WCHAR
*)GlobalLock(hClipboardData
);
174 _tcscpy_s(pchData
, sClipdata
.GetLength()+1, (LPCWSTR
)sClipdata
);
175 GlobalUnlock(hClipboardData
);
176 if (SetClipboardData(CF_UNICODETEXT
, hClipboardData
))
178 // no need to also set CF_TEXT : the OS does this
188 bool CStringUtils::WriteDiffToClipboard(const CStringA
& sClipdata
, HWND hOwningWnd
)
190 UINT cFormat
= RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
193 CClipboardHelper clipboardHelper
;
194 if (clipboardHelper
.Open(hOwningWnd
))
197 HGLOBAL hClipboardData
= CClipboardHelper::GlobalAlloc(sClipdata
.GetLength()+1);
200 char* pchData
= (char*)GlobalLock(hClipboardData
);
203 strcpy_s(pchData
, sClipdata
.GetLength()+1, (LPCSTR
)sClipdata
);
204 GlobalUnlock(hClipboardData
);
205 if (SetClipboardData(cFormat
,hClipboardData
)==NULL
)
209 if (SetClipboardData(CF_TEXT
,hClipboardData
))
219 bool CStringUtils::ReadStringFromTextFile(const CString
& path
, CString
& text
)
221 if (!PathFileExists(path
))
226 if (!file
.Open(path
, CFile::modeRead
| CFile::shareDenyWrite
))
229 CStringA filecontent
;
230 UINT filelength
= (UINT
)file
.GetLength();
231 int bytesread
= (int)file
.Read(filecontent
.GetBuffer(filelength
), filelength
);
232 filecontent
.ReleaseBuffer(bytesread
);
233 text
= CUnicodeUtils::GetUnicode(filecontent
);
236 catch (CFileException
* pE
)
244 #endif // #ifdef _MFC_VER
246 #if defined(CSTRING_AVAILABLE) || defined(_MFC_VER)
247 BOOL
CStringUtils::WildCardMatch(const CString
& wildcard
, const CString
& string
)
249 return _tcswildcmp(wildcard
, string
);
252 CString
CStringUtils::LinesWrap(const CString
& longstring
, int limit
/* = 80 */, bool bCompactPaths
/* = true */)
255 if ((longstring
.GetLength() < limit
) || (limit
== 0))
256 return longstring
; // no wrapping needed.
257 // now start breaking the string into lines
262 while ((linepos
= longstring
.Find('\n', linepos
)) >= 0)
264 temp
= longstring
.Mid(lineposold
, linepos
-lineposold
);
265 if ((linepos
+1)<longstring
.GetLength())
269 lineposold
= linepos
;
270 if (!retString
.IsEmpty())
271 retString
+= _T("\n");
272 retString
+= WordWrap(temp
, limit
, bCompactPaths
, false, 4);
274 temp
= longstring
.Mid(lineposold
);
276 retString
+= _T("\n");
277 retString
+= WordWrap(temp
, limit
, bCompactPaths
, false, 4);
282 CString
CStringUtils::WordWrap(const CString
& longstring
, int limit
, bool bCompactPaths
, bool bForceWrap
, int tabSize
)
284 int nLength
= longstring
.GetLength();
293 for (int i
= 0; i
< nLength
; ++i
)
295 if (i
-nLineStart
+tabOffset
>= limit
)
297 if (nLineEnd
== nLineStart
)
303 while ((i
< nLength
) && (longstring
[i
] != ' ') && (longstring
[i
] != '\t'))
310 CString longline
= longstring
.Mid(nLineStart
, nLineEnd
-nLineStart
).Left(MAX_PATH
-1);
311 if ((bCompactPaths
)&&(longline
.GetLength() < MAX_PATH
))
313 if (((!PathIsFileSpec(longline
))&&longline
.Find(':')<3)||(PathIsURL(longline
)))
316 PathCompactPathEx(buf
, longline
, limit
+1, 0);
320 retString
+= longline
;
323 retString
+= longstring
.Mid(nLineStart
, nLineEnd
-nLineStart
);
326 nLineStart
= nLineEnd
;
328 if (longstring
[i
] == ' ')
330 if (longstring
[i
] == '\t')
332 tabOffset
+= (tabSize
- i
% tabSize
);
338 CString longline
= longstring
.Mid(nLineStart
).Left(MAX_PATH
-1);
339 if ((bCompactPaths
)&&(longline
.GetLength() < MAX_PATH
))
341 if (((!PathIsFileSpec(longline
))&&longline
.Find(':')<3)||(PathIsURL(longline
)))
344 PathCompactPathEx(buf
, longline
, limit
+1, 0);
348 retString
+= longline
;
351 retString
+= longstring
.Mid(nLineStart
);
355 int CStringUtils::GetMatchingLength (const CString
& lhs
, const CString
& rhs
)
357 int lhsLength
= lhs
.GetLength();
358 int rhsLength
= rhs
.GetLength();
359 int maxResult
= min (lhsLength
, rhsLength
);
364 for (int i
= 0; i
< maxResult
; ++i
)
365 if (pLhs
[i
] != pRhs
[i
])
371 int CStringUtils::FastCompareNoCase (const CStringW
& lhs
, const CStringW
& rhs
)
373 // attempt latin-only comparison
375 INT_PTR count
= min (lhs
.GetLength(), rhs
.GetLength()+1);
376 const wchar_t* left
= lhs
;
377 const wchar_t* right
= rhs
;
378 for (const wchar_t* last
= left
+ count
+1; left
< last
; ++left
, ++right
)
380 int leftChar
= *left
;
381 int rightChar
= *right
;
383 int diff
= leftChar
- rightChar
;
386 // case-sensitive comparison found a difference
388 if ((leftChar
| rightChar
) >= 0x80)
390 // non-latin char -> fall back to CRT code
391 // (full comparison required as we might have
392 // skipped special chars / UTF plane selectors)
394 return _wcsicmp (lhs
, rhs
);
397 // normalize to lower case
399 if ((leftChar
>= 'A') && (leftChar
<= 'Z'))
400 leftChar
+= 'a' - 'A';
401 if ((rightChar
>= 'A') && (rightChar
<= 'Z'))
402 rightChar
+= 'a' - 'A';
406 diff
= leftChar
- rightChar
;
412 // must be equal (both ended with a 0)
416 #endif // #if defined(CSTRING_AVAILABLE) || defined(_MFC_VER)
418 bool CStringUtils::WriteStringToTextFile(const std::wstring
& path
, const std::wstring
& text
, bool bUTF8
/* = true */)
421 CAutoFile hFile
= CreateFile(path
.c_str(), GENERIC_WRITE
, FILE_SHARE_DELETE
, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
427 std::string buf
= CUnicodeUtils::StdGetUTF8(text
);
428 if (!WriteFile(hFile
, buf
.c_str(), (DWORD
)buf
.length(), &dwWritten
, NULL
))
435 if (!WriteFile(hFile
, text
.c_str(), (DWORD
)text
.length(), &dwWritten
, NULL
))
443 inline static void PipeToNull(TCHAR
* ptr
)
449 void CStringUtils::PipesToNulls(TCHAR
* buffer
, size_t length
)
451 TCHAR
* ptr
= buffer
+ length
;
452 while (ptr
!= buffer
)
459 void CStringUtils::PipesToNulls(TCHAR
* buffer
)
469 #define IsCharNumeric(C) (!IsCharAlpha(C) && IsCharAlphaNumeric(C))
472 #if defined(_DEBUG) && defined(_MFC_VER)
473 // Some test cases for these classes
474 static class StringUtilsTest
479 CString longline
= _T("this is a test of how a string can be splitted into several lines");
480 CString splittedline
= CStringUtils::WordWrap(longline
, 10, true, false, 4);
481 ATLTRACE(_T("WordWrap:\n%s\n"), splittedline
);
482 splittedline
= CStringUtils::LinesWrap(longline
, 10, true);
483 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);
484 longline
= _T("c:\\this_is_a_very_long\\path_on_windows and of course some other words added to make the line longer");
485 splittedline
= CStringUtils::WordWrap(longline
, 10, true, false, 4);
486 ATLTRACE(_T("WordWrap:\n%s\n"), splittedline
);
487 splittedline
= CStringUtils::LinesWrap(longline
, 10);
488 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);
489 longline
= _T("Forced failure in https://myserver.com/a_long_url_to_split PROPFIND error");
490 splittedline
= CStringUtils::WordWrap(longline
, 20, true, false, 4);
491 ATLTRACE(_T("WordWrap:\n%s\n"), splittedline
);
492 splittedline
= CStringUtils::LinesWrap(longline
, 20, true);
493 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);
494 longline
= _T("Forced\nfailure in https://myserver.com/a_long_url_to_split PROPFIND\nerror");
495 splittedline
= CStringUtils::WordWrap(longline
, 40, true, false, 4);
496 ATLTRACE(_T("WordWrap:\n%s\n"), splittedline
);
497 splittedline
= CStringUtils::LinesWrap(longline
, 40);
498 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);
499 longline
= _T("Failed to add file\nc:\\export\\spare\\Devl-JBoss\\development\\head\\src\\something\\CoreApplication\\somethingelse\\src\\com\\yetsomthingelse\\shipper\\DAO\\ShipmentInfoDAO1.java\nc:\\export\\spare\\Devl-JBoss\\development\\head\\src\\something\\CoreApplication\\somethingelse\\src\\com\\yetsomthingelse\\shipper\\DAO\\ShipmentInfoDAO2.java");
500 splittedline
= CStringUtils::WordWrap(longline
, 80, true, false, 4);
501 ATLTRACE(_T("WordWrap:\n%s\n"), splittedline
);
502 splittedline
= CStringUtils::LinesWrap(longline
);
503 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);
504 longline
= _T("The commit comment is not properly formatted.\nFormat:\n Field 1 : Field 2 : Field 3\nWhere:\nField 1 - Team Name|Triage|Merge|Goal\nField 2 - V1 Backlog Item ID|Triage Number|SVNBranch|Goal Name\nField 3 - Description of change\nExamples:\n\nTeam Gamma : B-12345 : Changed some code\n Triage : 123 : Fixed production release bug\n Merge : sprint0812 : Merged sprint0812 into prod\n Goal : Implement Pre-Commit Hook : Commit message hook impl");
505 splittedline
= CStringUtils::LinesWrap(longline
, 80);
506 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);