1 // TortoiseGit - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseGit
\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 "TortoiseProc.h"
\r
21 #include "UnicodeUtils.h"
\r
22 #include "ProjectProperties.h"
\r
23 //#include "GitProperties.h"
\r
24 #include "TGitPath.h"
\r
27 using namespace std;
\r
30 ProjectProperties::ProjectProperties(void)
\r
33 bWarnIfNoIssue = FALSE;
\r
34 nLogWidthMarker = 0;
\r
36 nMinLockMsgSize = 0;
\r
37 bFileListInEnglish = TRUE;
\r
39 lProjectLanguage = 0;
\r
42 ProjectProperties::~ProjectProperties(void)
\r
47 BOOL ProjectProperties::ReadPropsPathList(const CTGitPathList& pathList)
\r
49 for(int nPath = 0; nPath < pathList.GetCount(); nPath++)
\r
51 if (ReadProps(pathList[nPath]))
\r
59 BOOL ProjectProperties::ReadProps(CTGitPath path)
\r
61 BOOL bFoundBugtraqLabel = FALSE;
\r
62 BOOL bFoundBugtraqMessage = FALSE;
\r
63 BOOL bFoundBugtraqNumber = FALSE;
\r
64 BOOL bFoundBugtraqLogRe = FALSE;
\r
65 BOOL bFoundBugtraqURL = FALSE;
\r
66 BOOL bFoundBugtraqWarnIssue = FALSE;
\r
67 BOOL bFoundBugtraqAppend = FALSE;
\r
68 BOOL bFoundLogWidth = FALSE;
\r
69 BOOL bFoundLogTemplate = FALSE;
\r
70 BOOL bFoundMinLogSize = FALSE;
\r
71 BOOL bFoundMinLockMsgSize = FALSE;
\r
72 BOOL bFoundFileListEnglish = FALSE;
\r
73 BOOL bFoundProjectLanguage = FALSE;
\r
74 BOOL bFoundUserFileProps = FALSE;
\r
75 BOOL bFoundUserDirProps = FALSE;
\r
76 BOOL bFoundWebViewRev = FALSE;
\r
77 BOOL bFoundWebViewPathRev = FALSE;
\r
78 BOOL bFoundAutoProps = FALSE;
\r
79 BOOL bFoundLogSummary = FALSE;
\r
81 if (!path.IsDirectory())
\r
82 path = path.GetContainingDirectory();
\r
86 GitProperties props(path, GitRev::REV_WC, false);
\r
87 for (int i=0; i<props.GetCount(); ++i)
\r
89 CString sPropName = props.GetItemName(i).c_str();
\r
90 CString sPropVal = CUnicodeUtils::GetUnicode(((char *)props.GetItemValue(i).c_str()));
\r
91 if ((!bFoundBugtraqLabel)&&(sPropName.Compare(BUGTRAQPROPNAME_LABEL)==0))
\r
94 bFoundBugtraqLabel = TRUE;
\r
96 if ((!bFoundBugtraqMessage)&&(sPropName.Compare(BUGTRAQPROPNAME_MESSAGE)==0))
\r
98 sMessage = sPropVal;
\r
99 bFoundBugtraqMessage = TRUE;
\r
101 if ((!bFoundBugtraqNumber)&&(sPropName.Compare(BUGTRAQPROPNAME_NUMBER)==0))
\r
105 val = val.Trim(_T(" \n\r\t"));
\r
106 if ((val.CompareNoCase(_T("false"))==0)||(val.CompareNoCase(_T("no"))==0))
\r
110 bFoundBugtraqNumber = TRUE;
\r
112 if ((!bFoundBugtraqLogRe)&&(sPropName.Compare(BUGTRAQPROPNAME_LOGREGEX)==0))
\r
114 sCheckRe = sPropVal;
\r
115 if (sCheckRe.Find('\n')>=0)
\r
117 sBugIDRe = sCheckRe.Mid(sCheckRe.Find('\n')).Trim();
\r
118 sCheckRe = sCheckRe.Left(sCheckRe.Find('\n')).Trim();
\r
120 if (!sCheckRe.IsEmpty())
\r
122 sCheckRe = sCheckRe.Trim();
\r
124 bFoundBugtraqLogRe = TRUE;
\r
126 if ((!bFoundBugtraqURL)&&(sPropName.Compare(BUGTRAQPROPNAME_URL)==0))
\r
129 bFoundBugtraqURL = TRUE;
\r
131 if ((!bFoundBugtraqWarnIssue)&&(sPropName.Compare(BUGTRAQPROPNAME_WARNIFNOISSUE)==0))
\r
135 val = val.Trim(_T(" \n\r\t"));
\r
136 if ((val.CompareNoCase(_T("true"))==0)||(val.CompareNoCase(_T("yes"))==0))
\r
137 bWarnIfNoIssue = TRUE;
\r
139 bWarnIfNoIssue = FALSE;
\r
140 bFoundBugtraqWarnIssue = TRUE;
\r
142 if ((!bFoundBugtraqAppend)&&(sPropName.Compare(BUGTRAQPROPNAME_APPEND)==0))
\r
146 val = val.Trim(_T(" \n\r\t"));
\r
147 if ((val.CompareNoCase(_T("true"))==0)||(val.CompareNoCase(_T("yes"))==0))
\r
151 bFoundBugtraqAppend = TRUE;
\r
153 if ((!bFoundLogWidth)&&(sPropName.Compare(PROJECTPROPNAME_LOGWIDTHLINE)==0))
\r
157 if (!val.IsEmpty())
\r
159 nLogWidthMarker = _ttoi(val);
\r
161 bFoundLogWidth = TRUE;
\r
163 if ((!bFoundLogTemplate)&&(sPropName.Compare(PROJECTPROPNAME_LOGTEMPLATE)==0))
\r
165 sLogTemplate = sPropVal;
\r
166 sLogTemplate.Replace(_T("\r"), _T(""));
\r
167 sLogTemplate.Replace(_T("\n"), _T("\r\n"));
\r
168 bFoundLogTemplate = TRUE;
\r
170 if ((!bFoundMinLogSize)&&(sPropName.Compare(PROJECTPROPNAME_LOGMINSIZE)==0))
\r
174 if (!val.IsEmpty())
\r
176 nMinLogSize = _ttoi(val);
\r
178 bFoundMinLogSize = TRUE;
\r
180 if ((!bFoundMinLockMsgSize)&&(sPropName.Compare(PROJECTPROPNAME_LOCKMSGMINSIZE)==0))
\r
184 if (!val.IsEmpty())
\r
186 nMinLockMsgSize = _ttoi(val);
\r
188 bFoundMinLockMsgSize = TRUE;
\r
190 if ((!bFoundFileListEnglish)&&(sPropName.Compare(PROJECTPROPNAME_LOGFILELISTLANG)==0))
\r
194 val = val.Trim(_T(" \n\r\t"));
\r
195 if ((val.CompareNoCase(_T("false"))==0)||(val.CompareNoCase(_T("no"))==0))
\r
196 bFileListInEnglish = TRUE;
\r
198 bFileListInEnglish = FALSE;
\r
199 bFoundFileListEnglish = TRUE;
\r
201 if ((!bFoundProjectLanguage)&&(sPropName.Compare(PROJECTPROPNAME_PROJECTLANGUAGE)==0))
\r
205 if (!val.IsEmpty())
\r
208 lProjectLanguage = _tcstol(val, &strEnd, 0);
\r
210 bFoundProjectLanguage = TRUE;
\r
212 if ((!bFoundUserFileProps)&&(sPropName.Compare(PROJECTPROPNAME_USERFILEPROPERTY)==0))
\r
214 sFPPath = sPropVal;
\r
215 sFPPath.Replace(_T("\r\n"), _T("\n"));
\r
216 bFoundUserFileProps = TRUE;
\r
218 if ((!bFoundUserDirProps)&&(sPropName.Compare(PROJECTPROPNAME_USERDIRPROPERTY)==0))
\r
220 sDPPath = sPropVal;
\r
221 sDPPath.Replace(_T("\r\n"), _T("\n"));
\r
222 bFoundUserDirProps = TRUE;
\r
224 if ((!bFoundAutoProps)&&(sPropName.Compare(PROJECTPROPNAME_AUTOPROPS)==0))
\r
226 sAutoProps = sPropVal;
\r
227 sAutoProps.Replace(_T("\r\n"), _T("\n"));
\r
228 bFoundAutoProps = TRUE;
\r
230 if ((!bFoundWebViewRev)&&(sPropName.Compare(PROJECTPROPNAME_WEBVIEWER_REV)==0))
\r
232 sWebViewerRev = sPropVal;
\r
233 bFoundWebViewRev = TRUE;
\r
235 if ((!bFoundWebViewPathRev)&&(sPropName.Compare(PROJECTPROPNAME_WEBVIEWER_PATHREV)==0))
\r
237 sWebViewerPathRev = sPropVal;
\r
238 bFoundWebViewPathRev = TRUE;
\r
240 if ((!bFoundLogSummary)&&(sPropName.Compare(PROJECTPROPNAME_LOGSUMMARY)==0))
\r
242 sLogSummaryRe = sPropVal;
\r
243 bFoundLogSummary = TRUE;
\r
246 if (PathIsRoot(path.GetWinPath()))
\r
249 path = path.GetContainingDirectory();
\r
250 if ((!path.HasAdminDir())||(path.IsEmpty()))
\r
252 if (bFoundBugtraqLabel | bFoundBugtraqMessage | bFoundBugtraqNumber
\r
253 | bFoundBugtraqURL | bFoundBugtraqWarnIssue | bFoundLogWidth
\r
254 | bFoundLogTemplate | bFoundBugtraqLogRe | bFoundMinLockMsgSize
\r
255 | bFoundUserFileProps | bFoundUserDirProps | bFoundAutoProps
\r
256 | bFoundWebViewRev | bFoundWebViewPathRev | bFoundLogSummary)
\r
265 return FALSE; //never reached
\r
268 CString ProjectProperties::GetBugIDFromLog(CString& msg)
\r
272 if (!sMessage.IsEmpty())
\r
275 CString sFirstPart;
\r
278 if (sMessage.Find(_T("%BUGID%"))<0)
\r
280 sFirstPart = sMessage.Left(sMessage.Find(_T("%BUGID%")));
\r
281 sLastPart = sMessage.Mid(sMessage.Find(_T("%BUGID%"))+7);
\r
282 msg.TrimRight('\n');
\r
283 if (msg.ReverseFind('\n')>=0)
\r
286 sBugLine = msg.Mid(msg.ReverseFind('\n')+1);
\r
289 sBugLine = msg.Left(msg.Find('\n'));
\r
297 // find out if the message consists only of numbers
\r
298 bool bOnlyNumbers = true;
\r
299 for (int i=0; i<msg.GetLength(); ++i)
\r
301 if (!_istdigit(msg[i]))
\r
303 bOnlyNumbers = false;
\r
313 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)
\r
315 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)
\r
317 if (sBugLine.IsEmpty())
\r
319 if (msg.Find('\n')>=0)
\r
320 sBugLine = msg.Left(msg.Find('\n'));
\r
321 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)
\r
323 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)
\r
327 if (sBugLine.IsEmpty())
\r
329 sBugID = sBugLine.Mid(sFirstPart.GetLength(), sBugLine.GetLength() - sFirstPart.GetLength() - sLastPart.GetLength());
\r
332 msg = msg.Mid(sBugLine.GetLength());
\r
333 msg.TrimLeft('\n');
\r
337 msg = msg.Left(msg.GetLength()-sBugLine.GetLength());
\r
338 msg.TrimRight('\n');
\r
344 BOOL ProjectProperties::FindBugID(const CString& msg, CWnd * pWnd)
\r
346 size_t offset1 = 0;
\r
347 size_t offset2 = 0;
\r
348 bool bFound = false;
\r
350 if (sUrl.IsEmpty())
\r
353 // first use the checkre string to find bug ID's in the message
\r
354 if (!sCheckRe.IsEmpty())
\r
356 if (!sBugIDRe.IsEmpty())
\r
359 // match with two regex strings (without grouping!)
\r
362 const tr1::wregex regCheck(sCheckRe);
\r
363 const tr1::wregex regBugID(sBugIDRe);
\r
364 const tr1::wsregex_iterator end;
\r
366 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)
\r
368 // (*it)[0] is the matched string
\r
369 wstring matchedString = (*it)[0];
\r
370 ptrdiff_t matchpos = it->position(0);
\r
371 for (tr1::wsregex_iterator it2(matchedString.begin(), matchedString.end(), regBugID); it2 != end; ++it2)
\r
373 ATLTRACE(_T("matched id : %s\n"), (*it2)[0].str().c_str());
\r
374 ptrdiff_t matchposID = it2->position(0);
\r
375 CHARRANGE range = {matchpos+matchposID, matchpos+matchposID+(*it2)[0].str().size()};
\r
376 pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);
\r
377 CHARFORMAT2 format;
\r
378 SecureZeroMemory(&format, sizeof(CHARFORMAT2));
\r
379 format.cbSize = sizeof(CHARFORMAT2);
\r
380 format.dwMask = CFM_LINK;
\r
381 format.dwEffects = CFE_LINK;
\r
382 pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
\r
387 catch (exception) {}
\r
393 const tr1::wregex regCheck(sCheckRe);
\r
394 const tr1::wsregex_iterator end;
\r
396 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)
\r
398 const tr1::wsmatch match = *it;
\r
399 // we define group 1 as the whole issue text and
\r
400 // group 2 as the bug ID
\r
401 if (match.size() >= 2)
\r
403 ATLTRACE(_T("matched id : %s\n"), wstring(match[1]).c_str());
\r
404 CHARRANGE range = {match[1].first-s.begin(), match[1].second-s.begin()};
\r
405 pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);
\r
406 CHARFORMAT2 format;
\r
407 SecureZeroMemory(&format, sizeof(CHARFORMAT2));
\r
408 format.cbSize = sizeof(CHARFORMAT2);
\r
409 format.dwMask = CFM_LINK;
\r
410 format.dwEffects = CFE_LINK;
\r
411 pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
\r
416 catch (exception) {}
\r
419 else if ((!bFound)&&(!sMessage.IsEmpty()))
\r
422 CString sFirstPart;
\r
425 if (sMessage.Find(_T("%BUGID%"))<0)
\r
427 sFirstPart = sMessage.Left(sMessage.Find(_T("%BUGID%")));
\r
428 sLastPart = sMessage.Mid(sMessage.Find(_T("%BUGID%"))+7);
\r
429 CString sMsg = msg;
\r
430 sMsg.TrimRight('\n');
\r
431 if (sMsg.ReverseFind('\n')>=0)
\r
434 sBugLine = sMsg.Mid(sMsg.ReverseFind('\n')+1);
\r
437 sBugLine = sMsg.Left(sMsg.Find('\n'));
\r
443 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)
\r
445 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)
\r
447 if (sBugLine.IsEmpty())
\r
449 if (sMsg.Find('\n')>=0)
\r
450 sBugLine = sMsg.Left(sMsg.Find('\n'));
\r
451 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)
\r
453 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)
\r
457 if (sBugLine.IsEmpty())
\r
459 CString sBugIDPart = sBugLine.Mid(sFirstPart.GetLength(), sBugLine.GetLength() - sFirstPart.GetLength() - sLastPart.GetLength());
\r
460 if (sBugIDPart.IsEmpty())
\r
462 //the bug id part can contain several bug id's, separated by commas
\r
464 offset1 = sMsg.GetLength() - sBugLine.GetLength() + sFirstPart.GetLength();
\r
466 offset1 = sFirstPart.GetLength();
\r
467 sBugIDPart.Trim(_T(","));
\r
468 while (sBugIDPart.Find(',')>=0)
\r
470 offset2 = offset1 + sBugIDPart.Find(',');
\r
471 CHARRANGE range = {(LONG)offset1, (LONG)offset2};
\r
472 pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);
\r
473 CHARFORMAT2 format;
\r
474 SecureZeroMemory(&format, sizeof(CHARFORMAT2));
\r
475 format.cbSize = sizeof(CHARFORMAT2);
\r
476 format.dwMask = CFM_LINK;
\r
477 format.dwEffects = CFE_LINK;
\r
478 pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
\r
479 sBugIDPart = sBugIDPart.Mid(sBugIDPart.Find(',')+1);
\r
480 offset1 = offset2 + 1;
\r
482 offset2 = offset1 + sBugIDPart.GetLength();
\r
483 CHARRANGE range = {(LONG)offset1, (LONG)offset2};
\r
484 pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);
\r
485 CHARFORMAT2 format;
\r
486 SecureZeroMemory(&format, sizeof(CHARFORMAT2));
\r
487 format.cbSize = sizeof(CHARFORMAT2);
\r
488 format.dwMask = CFM_LINK;
\r
489 format.dwEffects = CFE_LINK;
\r
490 pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
\r
496 CString ProjectProperties::FindBugID(const CString& msg)
\r
498 size_t offset1 = 0;
\r
499 size_t offset2 = 0;
\r
500 bool bFound = false;
\r
504 std::set<CString> bugIDs;
\r
506 // first use the checkre string to find bug ID's in the message
\r
507 if (!sCheckRe.IsEmpty())
\r
509 if (!sBugIDRe.IsEmpty())
\r
511 // match with two regex strings (without grouping!)
\r
514 const tr1::wregex regCheck(sCheckRe);
\r
515 const tr1::wregex regBugID(sBugIDRe);
\r
516 const tr1::wsregex_iterator end;
\r
518 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)
\r
520 // (*it)[0] is the matched string
\r
521 wstring matchedString = (*it)[0];
\r
522 for (tr1::wsregex_iterator it2(matchedString.begin(), matchedString.end(), regBugID); it2 != end; ++it2)
\r
524 ATLTRACE(_T("matched id : %s\n"), (*it2)[0].str().c_str());
\r
525 bugIDs.insert(CString((*it2)[0].str().c_str()));
\r
529 catch (exception) {}
\r
535 const tr1::wregex regCheck(sCheckRe);
\r
536 const tr1::wsregex_iterator end;
\r
538 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)
\r
540 const tr1::wsmatch match = *it;
\r
541 // we define group 1 as the whole issue text and
\r
542 // group 2 as the bug ID
\r
543 if (match.size() >= 2)
\r
545 ATLTRACE(_T("matched id : %s\n"), wstring(match[1]).c_str());
\r
546 bugIDs.insert(CString(wstring(match[1]).c_str()));
\r
550 catch (exception) {}
\r
553 else if ((!bFound)&&(!sMessage.IsEmpty()))
\r
556 CString sFirstPart;
\r
559 if (sMessage.Find(_T("%BUGID%"))<0)
\r
561 sFirstPart = sMessage.Left(sMessage.Find(_T("%BUGID%")));
\r
562 sLastPart = sMessage.Mid(sMessage.Find(_T("%BUGID%"))+7);
\r
563 CString sMsg = msg;
\r
564 sMsg.TrimRight('\n');
\r
565 if (sMsg.ReverseFind('\n')>=0)
\r
568 sBugLine = sMsg.Mid(sMsg.ReverseFind('\n')+1);
\r
571 sBugLine = sMsg.Left(sMsg.Find('\n'));
\r
577 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)
\r
579 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)
\r
581 if (sBugLine.IsEmpty())
\r
583 if (sMsg.Find('\n')>=0)
\r
584 sBugLine = sMsg.Left(sMsg.Find('\n'));
\r
585 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)
\r
587 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)
\r
591 if (sBugLine.IsEmpty())
\r
593 CString sBugIDPart = sBugLine.Mid(sFirstPart.GetLength(), sBugLine.GetLength() - sFirstPart.GetLength() - sLastPart.GetLength());
\r
594 if (sBugIDPart.IsEmpty())
\r
596 //the bug id part can contain several bug id's, separated by commas
\r
598 offset1 = sMsg.GetLength() - sBugLine.GetLength() + sFirstPart.GetLength();
\r
600 offset1 = sFirstPart.GetLength();
\r
601 sBugIDPart.Trim(_T(","));
\r
602 while (sBugIDPart.Find(',')>=0)
\r
604 offset2 = offset1 + sBugIDPart.Find(',');
\r
605 CHARRANGE range = {(LONG)offset1, (LONG)offset2};
\r
606 bugIDs.insert(msg.Mid(range.cpMin, range.cpMax-range.cpMin));
\r
607 sBugIDPart = sBugIDPart.Mid(sBugIDPart.Find(',')+1);
\r
608 offset1 = offset2 + 1;
\r
610 offset2 = offset1 + sBugIDPart.GetLength();
\r
611 CHARRANGE range = {(LONG)offset1, (LONG)offset2};
\r
612 bugIDs.insert(msg.Mid(range.cpMin, range.cpMax-range.cpMin));
\r
615 for (std::set<CString>::iterator it = bugIDs.begin(); it != bugIDs.end(); ++it)
\r
624 CString ProjectProperties::GetBugIDUrl(const CString& sBugID)
\r
627 if (sUrl.IsEmpty())
\r
629 if (!sMessage.IsEmpty() || !sCheckRe.IsEmpty())
\r
632 ret.Replace(_T("%BUGID%"), sBugID);
\r
637 BOOL ProjectProperties::CheckBugID(const CString& sID)
\r
639 if (!sCheckRe.IsEmpty()&&(!bNumber)&&!sID.IsEmpty())
\r
641 CString sBugID = sID;
\r
642 sBugID.Replace(_T(", "), _T(","));
\r
643 sBugID.Replace(_T(" ,"), _T(","));
\r
644 CString sMsg = sMessage;
\r
645 sMsg.Replace(_T("%BUGID%"), sBugID);
\r
646 return HasBugID(sMsg);
\r
650 // check if the revision actually _is_ a number
\r
651 // or a list of numbers separated by colons
\r
653 int len = sID.GetLength();
\r
654 for (int i=0; i<len; ++i)
\r
657 if ((c < '0')&&(c != ','))
\r
668 BOOL ProjectProperties::HasBugID(const CString& sMessage)
\r
670 if (!sCheckRe.IsEmpty())
\r
674 tr1::wregex rx(sCheckRe);
\r
675 return tr1::regex_search((LPCTSTR)sMessage, rx);
\r
677 catch (exception) {}
\r
682 void ProjectProperties::InsertAutoProps(Git_config_t *cfg)
\r
684 // every line is an autoprop
\r
685 CString sPropsData = sAutoProps;
\r
686 bool bEnableAutoProps = false;
\r
687 while (!sPropsData.IsEmpty())
\r
689 int pos = sPropsData.Find('\n');
\r
690 CString sLine = pos >= 0 ? sPropsData.Left(pos) : sPropsData;
\r
692 if (!sLine.IsEmpty())
\r
694 // format is '*.something = property=value;property=value;....'
\r
695 // find first '=' char
\r
696 int equalpos = sLine.Find('=');
\r
697 if ((equalpos >= 0)&&(sLine[0] != '#'))
\r
699 CString key = sLine.Left(equalpos);
\r
700 CString value = sLine.Mid(equalpos);
\r
701 key.Trim(_T(" ="));
\r
702 value.Trim(_T(" ="));
\r
703 Git_config_set(cfg, Git_CONFIG_SECTION_AUTO_PROPS, (LPCSTR)CUnicodeUtils::GetUTF8(key), (LPCSTR)CUnicodeUtils::GetUTF8(value));
\r
704 bEnableAutoProps = true;
\r
708 sPropsData = sPropsData.Mid(pos).Trim();
\r
710 sPropsData.Empty();
\r
712 if (bEnableAutoProps)
\r
713 Git_config_set(cfg, Git_CONFIG_SECTION_MISCELLANY, Git_CONFIG_OPTION_ENABLE_AUTO_PROPS, "yes");
\r
717 bool ProjectProperties::AddAutoProps(const CTGitPath& path)
\r
719 if (!path.IsDirectory())
\r
720 return true; // no error, but nothing to do
\r
724 char buf[1024] = {0};
\r
726 GitProperties props(path, GitRev::REV_WC, false);
\r
727 if (!sLabel.IsEmpty())
\r
728 bRet = props.Add(BUGTRAQPROPNAME_LABEL, WideToMultibyte((LPCTSTR)sLabel)) && bRet;
\r
729 if (!sMessage.IsEmpty())
\r
730 bRet = props.Add(BUGTRAQPROPNAME_MESSAGE, WideToMultibyte((LPCTSTR)sMessage)) && bRet;
\r
732 bRet = props.Add(BUGTRAQPROPNAME_NUMBER, "false") && bRet;
\r
733 if (!sCheckRe.IsEmpty())
\r
734 bRet = props.Add(BUGTRAQPROPNAME_LOGREGEX, WideToMultibyte((LPCTSTR)(sCheckRe + _T("\n") + sBugIDRe))) && bRet;
\r
735 if (!sUrl.IsEmpty())
\r
736 bRet = props.Add(BUGTRAQPROPNAME_URL, WideToMultibyte((LPCTSTR)sUrl)) && bRet;
\r
737 if (bWarnIfNoIssue)
\r
738 bRet = props.Add(BUGTRAQPROPNAME_WARNIFNOISSUE, "true") && bRet;
\r
740 bRet = props.Add(BUGTRAQPROPNAME_APPEND, "false") && bRet;
\r
741 if (nLogWidthMarker)
\r
743 sprintf_s(buf, sizeof(buf), "%ld", nLogWidthMarker);
\r
744 bRet = props.Add(PROJECTPROPNAME_LOGWIDTHLINE, buf) && bRet;
\r
746 if (!sLogTemplate.IsEmpty())
\r
747 bRet = props.Add(PROJECTPROPNAME_LOGTEMPLATE, WideToMultibyte((LPCTSTR)sLogTemplate)) && bRet;
\r
750 sprintf_s(buf, sizeof(buf), "%ld", nMinLogSize);
\r
751 bRet = props.Add(PROJECTPROPNAME_LOGMINSIZE, buf) && bRet;
\r
753 if (nMinLockMsgSize)
\r
755 sprintf_s(buf, sizeof(buf), "%ld", nMinLockMsgSize);
\r
756 bRet = props.Add(PROJECTPROPNAME_LOCKMSGMINSIZE, buf) && bRet;
\r
758 if (!bFileListInEnglish)
\r
759 bRet = props.Add(PROJECTPROPNAME_LOGFILELISTLANG, "false") && bRet;
\r
760 if (lProjectLanguage)
\r
762 sprintf_s(buf, sizeof(buf), "%ld", lProjectLanguage);
\r
763 bRet = props.Add(PROJECTPROPNAME_PROJECTLANGUAGE, buf) && bRet;
\r
765 if (!sFPPath.IsEmpty())
\r
766 bRet = props.Add(PROJECTPROPNAME_USERFILEPROPERTY, WideToMultibyte((LPCTSTR)sFPPath)) && bRet;
\r
767 if (!sDPPath.IsEmpty())
\r
768 bRet = props.Add(PROJECTPROPNAME_USERDIRPROPERTY, WideToMultibyte((LPCTSTR)sDPPath)) && bRet;
\r
769 if (!sWebViewerRev.IsEmpty())
\r
770 bRet = props.Add(PROJECTPROPNAME_WEBVIEWER_REV, WideToMultibyte((LPCTSTR)sWebViewerRev)) && bRet;
\r
771 if (!sWebViewerPathRev.IsEmpty())
\r
772 bRet = props.Add(PROJECTPROPNAME_WEBVIEWER_PATHREV, WideToMultibyte((LPCTSTR)sWebViewerPathRev)) && bRet;
\r
773 if (!sAutoProps.IsEmpty())
\r
774 bRet = props.Add(PROJECTPROPNAME_AUTOPROPS, WideToMultibyte((LPCTSTR)sAutoProps)) && bRet;
\r
779 CString ProjectProperties::GetLogSummary(const CString& sMessage)
\r
783 if (!sLogSummaryRe.IsEmpty())
\r
787 const tr1::wregex regSum(sLogSummaryRe);
\r
788 const tr1::wsregex_iterator end;
\r
789 wstring s = sMessage;
\r
790 for (tr1::wsregex_iterator it(s.begin(), s.end(), regSum); it != end; ++it)
\r
792 const tr1::wsmatch match = *it;
\r
793 // we define the first group as the summary text
\r
794 if ((*it).size() >= 1)
\r
796 ATLTRACE(_T("matched summary : %s\n"), wstring(match[0]).c_str());
\r
797 sRet += (CString(wstring(match[1]).c_str()));
\r
801 catch (exception) {}
\r
809 static class PropTest
\r
814 CString msg = _T("this is a test logmessage: issue 222\nIssue #456, #678, 901 #456");
\r
815 CString sUrl = _T("http://tortoiseGit.tigris.org/issues/show_bug.cgi?id=%BUGID%");
\r
816 CString sCheckRe = _T("[Ii]ssue #?(\\d+)(,? ?#?(\\d+))+");
\r
817 CString sBugIDRe = _T("(\\d+)");
\r
818 ProjectProperties props;
\r
819 props.sCheckRe = _T("PAF-[0-9]+");
\r
820 props.sUrl = _T("http://tortoiseGit.tigris.org/issues/show_bug.cgi?id=%BUGID%");
\r
821 CString sRet = props.FindBugID(_T("This is a test for PAF-88"));
\r
822 ATLASSERT(sRet.IsEmpty());
\r
823 props.sCheckRe = _T("[Ii]ssue #?(\\d+)");
\r
824 sRet = props.FindBugID(_T("Testing issue #99"));
\r
826 ATLASSERT(sRet.Compare(_T("99"))==0);
\r
827 props.sCheckRe = _T("[Ii]ssues?:?(\\s*(,|and)?\\s*#\\d+)+");
\r
828 props.sBugIDRe = _T("(\\d+)");
\r
829 props.sUrl = _T("http://tortoiseGit.tigris.org/issues/show_bug.cgi?id=%BUGID%");
\r
830 sRet = props.FindBugID(_T("This is a test for Issue #7463,#666"));
\r
831 ATLASSERT(props.HasBugID(_T("This is a test for Issue #7463,#666")));
\r
832 ATLASSERT(!props.HasBugID(_T("This is a test for Issue 7463,666")));
\r
834 ATLASSERT(sRet.Compare(_T("666 7463"))==0);
\r
835 props.sCheckRe = _T("^\\[(\\d+)\\].*");
\r
836 props.sUrl = _T("http://tortoiseGit.tigris.org/issues/show_bug.cgi?id=%BUGID%");
\r
837 sRet = props.FindBugID(_T("[000815] some stupid programming error fixed"));
\r
839 ATLASSERT(sRet.Compare(_T("000815"))==0);
\r
840 props.sCheckRe = _T("\\[\\[(\\d+)\\]\\]\\]");
\r
841 props.sUrl = _T("http://tortoiseGit.tigris.org/issues/show_bug.cgi?id=%BUGID%");
\r
842 sRet = props.FindBugID(_T("test test [[000815]]] some stupid programming error fixed"));
\r
844 ATLASSERT(sRet.Compare(_T("000815"))==0);
\r
845 ATLASSERT(props.HasBugID(_T("test test [[000815]]] some stupid programming error fixed")));
\r
846 ATLASSERT(!props.HasBugID(_T("test test [000815]] some stupid programming error fixed")));
\r
847 props.sLogSummaryRe = _T("\\[SUMMARY\\]:(.*)");
\r
848 ATLASSERT(props.GetLogSummary(_T("[SUMMARY]: this is my summary")).Compare(_T("this is my summary"))==0);
\r