Fixed Issue #395: [BUG] Infomation error when "Switch the comparison"
[TortoiseGit.git] / src / Utils / StringUtils.cpp
blobebf54ee96fe161e7e642c43f7a1b3192d71812d9
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.
19 #include "StdAfx.h"
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 != '?'))
31 return 0;
33 wild++;
34 string++;
36 while (*string)
38 if (*wild == '*')
40 if (!*++wild)
42 return 1;
44 mp = wild;
45 cp = string+1;
47 else if ((*wild == *string) || (*wild == '?'))
49 wild++;
50 string++;
52 else
54 wild = mp;
55 string = cp++;
59 while (*wild == '*')
61 wild++;
63 return !*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 != '?'))
74 return 0;
76 wild++;
77 string++;
79 while (*string)
81 if (*wild == '*')
83 if (!*++wild)
85 return 1;
87 mp = wild;
88 cp = string+1;
90 else if ((*wild == *string) || (*wild == '?'))
92 wild++;
93 string++;
95 else
97 wild = mp;
98 string = cp++;
102 while (*wild == '*')
104 wild++;
106 return !*wild;
109 #ifdef _MFC_VER
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 */)
117 CString retString;
118 CStringArray arWords;
119 if (longstring.GetLength() < limit)
120 return longstring; // no wrapping needed.
121 // now start breaking the string into words
123 int linepos = 0;
124 int lineposold = 0;
125 CString temp;
126 while ((linepos = longstring.Find('\n', linepos)) >= 0)
128 temp = longstring.Mid(lineposold, linepos-lineposold);
129 if (temp.IsEmpty())
130 break;
131 if ((linepos+1)<longstring.GetLength())
132 linepos++;
133 lineposold = linepos;
134 if (!retString.IsEmpty())
135 retString += _T("\n");
136 retString += WordWrap(temp, limit, bCompactPaths);
138 temp = longstring.Mid(lineposold);
139 if (!temp.IsEmpty())
140 retString += _T("\n");
141 retString += WordWrap(temp, limit, bCompactPaths);
142 retString.Trim();
143 retString.Replace(_T("\n\n"), _T("\n"));
144 return retString;
147 CString CStringUtils::WordWrap(const CString& longstring, int limit /* = 80 */, bool bCompactPaths /* = true */)
149 CString retString;
150 if (longstring.GetLength() < limit)
151 return longstring; // no wrapping needed.
152 CString temp = longstring;
153 while (temp.GetLength() > limit)
155 int pos=0;
156 int oldpos=0;
157 while ((pos>=0)&&(temp.Find(' ', pos)<limit)&&(temp.Find(' ', pos)>0))
159 oldpos = pos;
160 pos = temp.Find(' ', pos+1);
162 if (oldpos==0)
163 oldpos = temp.Find(' ');
164 if (pos<0)
166 retString += temp;
167 temp.Empty();
169 else
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)))
176 TCHAR buf[MAX_PATH];
177 PathCompactPathEx(buf, longline, limit+1, 0);
178 longline = buf;
182 retString += longline;
183 if (oldpos >= 0)
184 temp = temp.Mid(oldpos+1);
185 else
186 temp.Empty();
188 retString += _T("\n");
189 pos = oldpos;
191 retString += temp;
192 retString.Trim();
193 return retString;
196 void CStringUtils::RemoveAccelerators(CString& text)
198 int pos = 0;
199 while ((pos=text.Find('&',pos))>=0)
201 if (text.GetLength() > (pos-1))
203 if (text.GetAt(pos+1)!=' ')
204 text.Delete(pos);
206 pos++;
211 bool CStringUtils::WriteAsciiStringToClipboard(const CStringA& sClipdata, LCID lcid, HWND hOwningWnd)
213 if (OpenClipboard(hOwningWnd))
215 EmptyClipboard();
216 HGLOBAL hClipboardData;
217 hClipboardData = GlobalAlloc(GMEM_DDESHARE, sClipdata.GetLength()+1);
218 if (hClipboardData)
220 char * pchData;
221 pchData = (char*)GlobalLock(hClipboardData);
222 if (pchData)
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);
231 *plcid = lcid;
232 GlobalUnlock(hlocmem);
233 SetClipboardData(CF_LOCALE, static_cast<HANDLE>(plcid));
234 CloseClipboard();
235 return true;
238 else
240 CloseClipboard();
241 return false;
244 else
246 CloseClipboard();
247 return false;
250 else
252 CloseClipboard();
253 return false;
255 CloseClipboard();
256 return false;
258 return false;
261 bool CStringUtils::WriteAsciiStringToClipboard(const CStringW& sClipdata, HWND hOwningWnd)
263 if (OpenClipboard(hOwningWnd))
265 EmptyClipboard();
266 HGLOBAL hClipboardData;
267 hClipboardData = GlobalAlloc(GMEM_DDESHARE, (sClipdata.GetLength()+1)*sizeof(WCHAR));
268 if (hClipboardData)
270 WCHAR * pchData;
271 pchData = (WCHAR*)GlobalLock(hClipboardData);
272 if (pchData)
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);
282 if (hClipboardDataA)
284 char * pchDataA;
285 pchDataA = (char*)GlobalLock(hClipboardDataA);
286 if (pchDataA)
288 strcpy_s(pchDataA, sClipdataA.GetLength()+1, (LPCSTR)sClipdataA);
289 if (GlobalUnlock(hClipboardDataA))
291 if (SetClipboardData(CF_TEXT, hClipboardDataA) != NULL)
293 CloseClipboard();
294 return true;
297 else
299 CloseClipboard();
300 return false;
303 else
305 CloseClipboard();
306 return false;
310 CloseClipboard();
311 return false;
314 else
316 CloseClipboard();
317 return false;
320 else
322 CloseClipboard();
323 return false;
326 else
328 CloseClipboard();
329 return false;
331 CloseClipboard();
332 return false;
334 return false;
337 bool CStringUtils::WriteDiffToClipboard(const CStringA& sClipdata, HWND hOwningWnd)
339 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
340 if (cFormat == 0)
341 return false;
342 if (OpenClipboard(hOwningWnd))
344 EmptyClipboard();
345 HGLOBAL hClipboardData;
346 hClipboardData = GlobalAlloc(GMEM_DDESHARE, sClipdata.GetLength()+1);
347 if (hClipboardData)
349 char * pchData;
350 pchData = (char*)GlobalLock(hClipboardData);
351 if (pchData)
353 strcpy_s(pchData, sClipdata.GetLength()+1, (LPCSTR)sClipdata);
354 if (GlobalUnlock(hClipboardData))
356 if (SetClipboardData(cFormat,hClipboardData)==NULL)
358 CloseClipboard();
359 return false;
361 if (SetClipboardData(CF_TEXT,hClipboardData)==NULL)
363 CloseClipboard();
364 return false;
367 else
369 CloseClipboard();
370 return false;
373 else
375 CloseClipboard();
376 return false;
379 else
381 CloseClipboard();
382 return false;
384 CloseClipboard();
385 return true;
387 return false;
390 bool CStringUtils::ReadStringFromTextFile(const CString& path, CString& text)
392 if (!PathFileExists(path))
393 return false;
396 CStdioFile file;
397 if (!file.Open(path, CFile::modeRead | CFile::shareDenyWrite))
398 return false;
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);
405 file.Close();
407 catch (CFileException* /*pE*/)
409 text.Empty();
411 return true;
414 #endif // #ifdef _MFC_VER
416 bool CStringUtils::WriteStringToTextFile(const std::wstring& path, const std::wstring& text, bool bUTF8 /* = true */)
418 DWORD dwWritten = 0;
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)
421 return false;
423 if (bUTF8)
425 std::string buf = CUnicodeUtils::StdGetUTF8(text);
426 if (!WriteFile(hFile, buf.c_str(), buf.length(), &dwWritten, NULL))
428 CloseHandle(hFile);
429 return false;
432 else
434 if (!WriteFile(hFile, text.c_str(), text.length(), &dwWritten, NULL))
436 CloseHandle(hFile);
437 return false;
440 CloseHandle(hFile);
441 return true;
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))
457 ++num_x_cnt;
458 ++num_y_cnt;
460 else
462 num_x_begin = CharNext(x_str);
463 num_y_begin = CharNext(y_str);
464 num_x_cnt = 0;
465 num_y_cnt = 0;
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))
479 ++num_x_cnt;
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))
489 ++num_y_cnt;
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' */ )
498 return -1;
499 num_y_begin = CharNext(num_y_begin);
500 --num_y_cnt;
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' */ )
506 return 1;
507 num_x_begin = CharNext(num_x_begin);
508 --num_x_cnt;
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);
514 if (cmp_result != 2)
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);
525 if (cmp_result != 2)
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;
533 #if defined(_DEBUG)
534 // Some test cases for these classes
535 static class StringUtilsTest
537 public:
538 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);
566 } StringUtilsTest;
568 #endif