Include version.h and CrashReport.h only where needed
[TortoiseGit.git] / src / Utils / StringUtils.cpp
blobf9e12c4bb919a66fd2ffcaf86632e262046f4394
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.
19 #include "stdafx.h"
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 != '?'))
33 return 0;
35 wild++;
36 string++;
38 while (*string)
40 if (*wild == '*')
42 if (!*++wild)
44 return 1;
46 mp = wild;
47 cp = string+1;
49 else if ((*wild == *string) || (*wild == '?'))
51 wild++;
52 string++;
54 else
56 wild = mp;
57 string = cp++;
61 while (*wild == '*')
63 wild++;
65 return !*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 != '?'))
76 return 0;
78 wild++;
79 string++;
81 while (*string)
83 if (*wild == '*')
85 if (!*++wild)
87 return 1;
89 mp = wild;
90 cp = string+1;
92 else if ((*wild == *string) || (*wild == '?'))
94 wild++;
95 string++;
97 else
99 wild = mp;
100 string = cp++;
104 while (*wild == '*')
106 wild++;
108 return !*wild;
111 #ifdef _MFC_VER
113 void CStringUtils::RemoveAccelerators(CString& text)
115 int pos = 0;
116 while ((pos=text.Find('&',pos))>=0)
118 if (text.GetLength() > (pos-1))
120 if (text.GetAt(pos+1)!=' ')
121 text.Delete(pos);
123 pos++;
127 bool CStringUtils::WriteAsciiStringToClipboard(const CStringA& sClipdata, LCID lcid, HWND hOwningWnd)
129 CClipboardHelper clipboardHelper;
130 if (clipboardHelper.Open(hOwningWnd))
132 EmptyClipboard();
133 HGLOBAL hClipboardData = CClipboardHelper::GlobalAlloc(sClipdata.GetLength()+1);
134 if (hClipboardData)
136 char* pchData = (char*)GlobalLock(hClipboardData);
137 if (pchData)
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));
144 if (hlocmem)
146 PLCID plcid = (PLCID)GlobalLock(hlocmem);
147 if (plcid)
149 *plcid = lcid;
150 SetClipboardData(CF_LOCALE, static_cast<HANDLE>(plcid));
152 GlobalUnlock(hlocmem);
154 return true;
159 return false;
162 bool CStringUtils::WriteAsciiStringToClipboard(const CStringW& sClipdata, HWND hOwningWnd)
164 CClipboardHelper clipboardHelper;
165 if (clipboardHelper.Open(hOwningWnd))
167 EmptyClipboard();
168 HGLOBAL hClipboardData = CClipboardHelper::GlobalAlloc((sClipdata.GetLength()+1)*sizeof(WCHAR));
169 if (hClipboardData)
171 WCHAR* pchData = (WCHAR*)GlobalLock(hClipboardData);
172 if (pchData)
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
179 // automatically.
180 return true;
185 return false;
188 bool CStringUtils::WriteDiffToClipboard(const CStringA& sClipdata, HWND hOwningWnd)
190 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
191 if (cFormat == 0)
192 return false;
193 CClipboardHelper clipboardHelper;
194 if (clipboardHelper.Open(hOwningWnd))
196 EmptyClipboard();
197 HGLOBAL hClipboardData = CClipboardHelper::GlobalAlloc(sClipdata.GetLength()+1);
198 if (hClipboardData)
200 char* pchData = (char*)GlobalLock(hClipboardData);
201 if (pchData)
203 strcpy_s(pchData, sClipdata.GetLength()+1, (LPCSTR)sClipdata);
204 GlobalUnlock(hClipboardData);
205 if (SetClipboardData(cFormat,hClipboardData)==NULL)
207 return false;
209 if (SetClipboardData(CF_TEXT,hClipboardData))
211 return true;
216 return false;
219 bool CStringUtils::ReadStringFromTextFile(const CString& path, CString& text)
221 if (!PathFileExists(path))
222 return false;
225 CStdioFile file;
226 if (!file.Open(path, CFile::modeRead | CFile::shareDenyWrite))
227 return false;
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);
234 file.Close();
236 catch (CFileException* pE)
238 text.Empty();
239 pE->Delete();
241 return true;
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 */)
254 CString retString;
255 if ((longstring.GetLength() < limit) || (limit == 0))
256 return longstring; // no wrapping needed.
257 // now start breaking the string into lines
259 int linepos = 0;
260 int lineposold = 0;
261 CString temp;
262 while ((linepos = longstring.Find('\n', linepos)) >= 0)
264 temp = longstring.Mid(lineposold, linepos-lineposold);
265 if ((linepos+1)<longstring.GetLength())
266 linepos++;
267 else
268 break;
269 lineposold = linepos;
270 if (!retString.IsEmpty())
271 retString += _T("\n");
272 retString += WordWrap(temp, limit, bCompactPaths, false, 4);
274 temp = longstring.Mid(lineposold);
275 if (!temp.IsEmpty())
276 retString += _T("\n");
277 retString += WordWrap(temp, limit, bCompactPaths, false, 4);
278 retString.Trim();
279 return retString;
282 CString CStringUtils::WordWrap(const CString& longstring, int limit, bool bCompactPaths, bool bForceWrap, int tabSize)
284 int nLength = longstring.GetLength();
285 CString retString;
287 if (limit < 0)
288 limit = 0;
290 int nLineStart = 0;
291 int nLineEnd = 0;
292 int tabOffset = 0;
293 for (int i = 0; i < nLength; ++i)
295 if (i-nLineStart+tabOffset >= limit)
297 if (nLineEnd == nLineStart)
299 if (bForceWrap)
300 nLineEnd = i;
301 else
303 while ((i < nLength) && (longstring[i] != ' ') && (longstring[i] != '\t'))
304 ++i;
305 nLineEnd = i;
308 if (bCompactPaths)
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)))
315 TCHAR buf[MAX_PATH];
316 PathCompactPathEx(buf, longline, limit+1, 0);
317 longline = buf;
320 retString += longline;
322 else
323 retString += longstring.Mid(nLineStart, nLineEnd-nLineStart);
324 retString += L"\n";
325 tabOffset = 0;
326 nLineStart = nLineEnd;
328 if (longstring[i] == ' ')
329 nLineEnd = i;
330 if (longstring[i] == '\t')
332 tabOffset += (tabSize - i % tabSize);
333 nLineEnd = i;
336 if (bCompactPaths)
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)))
343 TCHAR buf[MAX_PATH];
344 PathCompactPathEx(buf, longline, limit+1, 0);
345 longline = buf;
348 retString += longline;
350 else
351 retString += longstring.Mid(nLineStart);
353 return retString;
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);
361 LPCTSTR pLhs = lhs;
362 LPCTSTR pRhs = rhs;
364 for (int i = 0; i < maxResult; ++i)
365 if (pLhs[i] != pRhs[i])
366 return i;
368 return maxResult;
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;
384 if (diff != 0)
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';
404 // compare again
406 diff = leftChar - rightChar;
407 if (diff != 0)
408 return diff;
412 // must be equal (both ended with a 0)
414 return 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 */)
420 DWORD dwWritten = 0;
421 CAutoFile hFile = CreateFile(path.c_str(), GENERIC_WRITE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
422 if (!hFile)
423 return false;
425 if (bUTF8)
427 std::string buf = CUnicodeUtils::StdGetUTF8(text);
428 if (!WriteFile(hFile, buf.c_str(), (DWORD)buf.length(), &dwWritten, NULL))
430 return false;
433 else
435 if (!WriteFile(hFile, text.c_str(), (DWORD)text.length(), &dwWritten, NULL))
437 return false;
440 return true;
443 inline static void PipeToNull(TCHAR* ptr)
445 if (*ptr == '|')
446 *ptr = '\0';
449 void CStringUtils::PipesToNulls(TCHAR* buffer, size_t length)
451 TCHAR* ptr = buffer + length;
452 while (ptr != buffer)
454 PipeToNull(ptr);
455 ptr--;
459 void CStringUtils::PipesToNulls(TCHAR* buffer)
461 TCHAR* ptr = buffer;
462 while (*ptr != 0)
464 PipeToNull(ptr);
465 ptr++;
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
476 public:
477 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);
508 } StringUtilsTest;
510 #endif