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