1 // TortoiseSVN - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - 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"
23 int strwildcmp(const char *wild
, const char *string
)
25 const char *cp
= NULL
;
26 const char *mp
= NULL
;
27 while ((*string
) && (*wild
!= '*'))
29 if ((*wild
!= *string
) && (*wild
!= '?'))
47 else if ((*wild
== *string
) || (*wild
== '?'))
66 int wcswildcmp(const wchar_t *wild
, const wchar_t *string
)
68 const wchar_t *cp
= NULL
;
69 const wchar_t *mp
= NULL
;
70 while ((*string
) && (*wild
!= '*'))
72 if ((*wild
!= *string
) && (*wild
!= '?'))
90 else if ((*wild
== *string
) || (*wild
== '?'))
110 BOOL
CStringUtils::WildCardMatch(const CString
& wildcard
, const CString
& string
)
112 return _tcswildcmp(wildcard
, string
);
115 CString
CStringUtils::LinesWrap(const CString
& longstring
, int limit
/* = 80 */, bool bCompactPaths
/* = true */)
118 CStringArray arWords
;
119 if (longstring
.GetLength() < limit
)
120 return longstring
; // no wrapping needed.
121 // now start breaking the string into words
126 while ((linepos
= longstring
.Find('\n', linepos
)) >= 0)
128 temp
= longstring
.Mid(lineposold
, linepos
-lineposold
);
131 if ((linepos
+1)<longstring
.GetLength())
133 lineposold
= linepos
;
134 if (!retString
.IsEmpty())
135 retString
+= _T("\n");
136 retString
+= WordWrap(temp
, limit
, bCompactPaths
);
138 temp
= longstring
.Mid(lineposold
);
140 retString
+= _T("\n");
141 retString
+= WordWrap(temp
, limit
, bCompactPaths
);
143 retString
.Replace(_T("\n\n"), _T("\n"));
147 CString
CStringUtils::WordWrap(const CString
& longstring
, int limit
/* = 80 */, bool bCompactPaths
/* = true */)
150 if (longstring
.GetLength() < limit
)
151 return longstring
; // no wrapping needed.
152 CString temp
= longstring
;
153 while (temp
.GetLength() > limit
)
157 while ((pos
>=0)&&(temp
.Find(' ', pos
)<limit
)&&(temp
.Find(' ', pos
)>0))
160 pos
= temp
.Find(' ', pos
+1);
163 oldpos
= temp
.Find(' ');
171 CString longline
= oldpos
>= 0 ? temp
.Left(oldpos
+1) : temp
;
172 if ((bCompactPaths
)&&(longline
.GetLength() < MAX_PATH
))
174 if (((!PathIsFileSpec(longline
))&&longline
.Find(':')<3)||(PathIsURL(longline
)))
177 PathCompactPathEx(buf
, longline
, limit
+1, 0);
182 retString
+= longline
;
184 temp
= temp
.Mid(oldpos
+1);
188 retString
+= _T("\n");
196 void CStringUtils::RemoveAccelerators(CString
& text
)
199 while ((pos
=text
.Find('&',pos
))>=0)
201 if (text
.GetLength() > (pos
-1))
203 if (text
.GetAt(pos
+1)!=' ')
211 bool CStringUtils::WriteAsciiStringToClipboard(const CStringA
& sClipdata
, LCID lcid
, HWND hOwningWnd
)
213 if (OpenClipboard(hOwningWnd
))
216 HGLOBAL hClipboardData
;
217 hClipboardData
= GlobalAlloc(GMEM_DDESHARE
, sClipdata
.GetLength()+1);
221 pchData
= (char*)GlobalLock(hClipboardData
);
224 strcpy_s(pchData
, sClipdata
.GetLength()+1, (LPCSTR
)sClipdata
);
225 if (GlobalUnlock(hClipboardData
))
227 if (SetClipboardData(CF_TEXT
, hClipboardData
)==NULL
)
229 HANDLE hlocmem
= GlobalAlloc(GMEM_MOVEABLE
|GMEM_DDESHARE
, sizeof(LCID
));
230 PLCID plcid
= (PLCID
)GlobalLock(hlocmem
);
232 GlobalUnlock(hlocmem
);
233 SetClipboardData(CF_LOCALE
, static_cast<HANDLE
>(plcid
));
261 bool CStringUtils::WriteAsciiStringToClipboard(const CStringW
& sClipdata
, HWND hOwningWnd
)
263 if (OpenClipboard(hOwningWnd
))
266 HGLOBAL hClipboardData
;
267 hClipboardData
= GlobalAlloc(GMEM_DDESHARE
, (sClipdata
.GetLength()+1)*sizeof(WCHAR
));
271 pchData
= (WCHAR
*)GlobalLock(hClipboardData
);
274 _tcscpy_s(pchData
, sClipdata
.GetLength()+1, (LPCWSTR
)sClipdata
);
275 if (GlobalUnlock(hClipboardData
))
277 if (SetClipboardData(CF_UNICODETEXT
, hClipboardData
) != NULL
)
279 CStringA sClipdataA
= CStringA(sClipdata
);
280 HGLOBAL hClipboardDataA
;
281 hClipboardDataA
= GlobalAlloc(GMEM_DDESHARE
, sClipdataA
.GetLength()+1);
285 pchDataA
= (char*)GlobalLock(hClipboardDataA
);
288 strcpy_s(pchDataA
, sClipdataA
.GetLength()+1, (LPCSTR
)sClipdataA
);
289 if (GlobalUnlock(hClipboardDataA
))
291 if (SetClipboardData(CF_TEXT
, hClipboardDataA
) != NULL
)
337 bool CStringUtils::WriteDiffToClipboard(const CStringA
& sClipdata
, HWND hOwningWnd
)
339 UINT cFormat
= RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
342 if (OpenClipboard(hOwningWnd
))
345 HGLOBAL hClipboardData
;
346 hClipboardData
= GlobalAlloc(GMEM_DDESHARE
, sClipdata
.GetLength()+1);
350 pchData
= (char*)GlobalLock(hClipboardData
);
353 strcpy_s(pchData
, sClipdata
.GetLength()+1, (LPCSTR
)sClipdata
);
354 if (GlobalUnlock(hClipboardData
))
356 if (SetClipboardData(cFormat
,hClipboardData
)==NULL
)
361 if (SetClipboardData(CF_TEXT
,hClipboardData
)==NULL
)
390 bool CStringUtils::ReadStringFromTextFile(const CString
& path
, CString
& text
)
392 if (!PathFileExists(path
))
397 if (!file
.Open(path
, CFile::modeRead
| CFile::shareDenyWrite
))
400 CStringA filecontent
;
401 UINT filelength
= (UINT
)file
.GetLength();
402 int bytesread
= (int)file
.Read(filecontent
.GetBuffer(filelength
), filelength
);
403 filecontent
.ReleaseBuffer(bytesread
);
404 text
= CUnicodeUtils::GetUnicode(filecontent
);
407 catch (CFileException
* /*pE*/)
414 #endif // #ifdef _MFC_VER
416 bool CStringUtils::WriteStringToTextFile(const std::wstring
& path
, const std::wstring
& text
, bool bUTF8
/* = true */)
419 HANDLE hFile
= CreateFile(path
.c_str(), GENERIC_WRITE
, FILE_SHARE_DELETE
, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
420 if (hFile
== INVALID_HANDLE_VALUE
)
425 std::string buf
= CUnicodeUtils::StdGetUTF8(text
);
426 if (!WriteFile(hFile
, buf
.c_str(), buf
.length(), &dwWritten
, NULL
))
434 if (!WriteFile(hFile
, text
.c_str(), text
.length(), &dwWritten
, NULL
))
444 #define IsCharNumeric(C) (!IsCharAlpha(C) && IsCharAlphaNumeric(C))
446 int CStringUtils::CompareNumerical(LPCTSTR x_str
, LPCTSTR y_str
)
448 LPCTSTR num_x_begin
= x_str
, num_y_begin
= y_str
;
449 DWORD num_x_cnt
= 0, num_y_cnt
= 0;
450 int cs_cmp_result
= 2;
452 // skip same chars and remember last numeric part of strings
453 while ((*x_str
|| *y_str
) && CompareString(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
| NORM_IGNOREWIDTH
, x_str
, 1, y_str
, 1) == 2 /* equal */ )
455 if (IsCharNumeric(*x_str
))
462 num_x_begin
= CharNext(x_str
);
463 num_y_begin
= CharNext(y_str
);
466 if (cs_cmp_result
== 2)
467 cs_cmp_result
= CompareString(LOCALE_USER_DEFAULT
, NORM_IGNOREWIDTH
, x_str
, 1, y_str
, 1);
469 x_str
= CharNext(x_str
);
470 y_str
= CharNext(y_str
);
473 // parse numeric part of first arg
474 if (num_x_cnt
|| IsCharNumeric(*x_str
))
476 LPCTSTR x_str_tmp
= x_str
;
477 while (IsCharNumeric(*x_str_tmp
))
480 x_str_tmp
= CharNext(x_str_tmp
);
483 // parse numeric part of second arg
484 if (num_y_cnt
|| IsCharNumeric(*y_str
))
486 LPCTSTR y_str_tmp
= y_str
;
487 while (IsCharNumeric(*y_str_tmp
))
490 y_str_tmp
= CharNext(y_str_tmp
);
493 DWORD num_x_cnt_with_zeros
= num_x_cnt
, num_y_cnt_with_zeros
= num_y_cnt
;
495 while (num_x_cnt
< num_y_cnt
)
497 if (CompareString(LOCALE_USER_DEFAULT
, NORM_IGNOREWIDTH
, num_y_begin
, 1, TEXT("0"), 1) != 2 /* not equal to '0' */ )
499 num_y_begin
= CharNext(num_y_begin
);
503 while (num_x_cnt
> num_y_cnt
)
505 if (CompareString(LOCALE_USER_DEFAULT
, NORM_IGNOREWIDTH
, num_x_begin
, 1, TEXT("0"), 1) != 2 /* not equal to '0' */ )
507 num_x_begin
= CharNext(num_x_begin
);
511 // here num_x_cnt == num_y_cnt
512 int cmp_result
= CompareString(LOCALE_USER_DEFAULT
, NORM_IGNOREWIDTH
, num_x_begin
, num_x_cnt
, num_y_begin
, num_y_cnt
);
515 return cmp_result
- 2;
516 if (num_x_cnt_with_zeros
!= num_y_cnt_with_zeros
)
517 return num_x_cnt_with_zeros
< num_y_cnt_with_zeros
? -1 : 1;
518 if (cs_cmp_result
!= 2)
519 return cs_cmp_result
- 2;
523 // otherwise, compare literally
524 int cmp_result
= CompareString(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
| NORM_IGNOREWIDTH
, x_str
, -1, y_str
, -1);
526 return cmp_result
- 2;
527 if (cs_cmp_result
== 2)
528 cs_cmp_result
= CompareString(LOCALE_USER_DEFAULT
, NORM_IGNOREWIDTH
, x_str
, -1, y_str
, -1);
529 return cs_cmp_result
- 2;
534 // Some test cases for these classes
535 static class StringUtilsTest
540 CString longline
= _T("this is a test of how a string can be splitted into several lines");
541 CString splittedline
= CStringUtils::WordWrap(longline
, 10);
542 ATLTRACE(_T("WordWrap:\n%s\n"), splittedline
);
543 splittedline
= CStringUtils::LinesWrap(longline
, 10);
544 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);
545 longline
= _T("c:\\this_is_a_very_long\\path_on_windows and of course some other words added to make the line longer");
546 splittedline
= CStringUtils::WordWrap(longline
, 10);
547 ATLTRACE(_T("WordWrap:\n%s\n"), splittedline
);
548 splittedline
= CStringUtils::LinesWrap(longline
, 10);
549 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);
550 longline
= _T("Forced failure in https://myserver.com/a_long_url_to_split PROPFIND error");
551 splittedline
= CStringUtils::WordWrap(longline
, 20);
552 ATLTRACE(_T("WordWrap:\n%s\n"), splittedline
);
553 splittedline
= CStringUtils::LinesWrap(longline
, 20);
554 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);
555 longline
= _T("Forced\nfailure in https://myserver.com/a_long_url_to_split PROPFIND\nerror");
556 splittedline
= CStringUtils::WordWrap(longline
, 40);
557 ATLTRACE(_T("WordWrap:\n%s\n"), splittedline
);
558 splittedline
= CStringUtils::LinesWrap(longline
, 40);
559 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);
560 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");
561 splittedline
= CStringUtils::WordWrap(longline
);
562 ATLTRACE(_T("WordWrap:\n%s\n"), splittedline
);
563 splittedline
= CStringUtils::LinesWrap(longline
);
564 ATLTRACE(_T("LinesWrap:\n%s\n"), splittedline
);