OSDN Git Service

Add bugtraq to support commit dialog bug track.
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / ProjectProperties.cpp
1 // TortoiseGit - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseGit\r
4 \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
9 \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
14 \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
18 //\r
19 #include "StdAfx.h"\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
25 #include <regex>\r
26 #include "git.h"\r
27 \r
28 using namespace std;\r
29 \r
30 \r
31 ProjectProperties::ProjectProperties(void)\r
32 {\r
33         bNumber = TRUE;\r
34         bWarnIfNoIssue = FALSE;\r
35         nLogWidthMarker = 0;\r
36         nMinLogSize = 0;\r
37         nMinLockMsgSize = 0;\r
38         bFileListInEnglish = TRUE;\r
39         bAppend = TRUE;\r
40         lProjectLanguage = 0;\r
41 }\r
42 \r
43 ProjectProperties::~ProjectProperties(void)\r
44 {\r
45 }\r
46 \r
47 \r
48 BOOL ProjectProperties::ReadPropsPathList(const CTGitPathList& pathList)\r
49 {\r
50         for(int nPath = 0; nPath < pathList.GetCount(); nPath++)\r
51         {\r
52                 if (ReadProps(pathList[nPath]))\r
53                 {\r
54                         return TRUE;\r
55                 }\r
56         }\r
57         return FALSE;\r
58 }\r
59 \r
60 BOOL ProjectProperties::GetStringProps(CString &prop,TCHAR *key,bool bRemoveCR)\r
61 {\r
62         CString cmd,output;\r
63         output.Empty();\r
64 \r
65         cmd.Format(_T("git.exe config %s"),key);\r
66         int start = 0;\r
67         if(g_Git.Run(cmd,&output,CP_ACP))\r
68         {\r
69                 return FALSE;\r
70         }\r
71         if(bRemoveCR)\r
72                 prop = output.Tokenize(_T("\n"),start); \r
73         else\r
74                 prop = output;\r
75 \r
76         return TRUE;\r
77 \r
78 }\r
79 \r
80 BOOL ProjectProperties::GetBOOLProps(BOOL &b,TCHAR *key)\r
81 {\r
82         CString str,low;\r
83         if(!GetStringProps(str,key))\r
84                 return FALSE;\r
85 \r
86         low=str.MakeLower();\r
87         if(low == _T("true"))\r
88                 b=true;\r
89         else\r
90                 b=false;\r
91 \r
92         return true;\r
93 \r
94 }\r
95 BOOL ProjectProperties::ReadProps(CTGitPath path)\r
96 {\r
97         CString sPropVal;\r
98 \r
99         GetStringProps(this->sLabel,BUGTRAQPROPNAME_LABEL);\r
100         GetStringProps(this->sMessage,BUGTRAQPROPNAME_MESSAGE);\r
101 \r
102         GetBOOLProps(this->bWarnIfNoIssue,BUGTRAQPROPNAME_WARNIFNOISSUE);\r
103         GetBOOLProps(this->bNumber,BUGTRAQPROPNAME_NUMBER);\r
104         GetBOOLProps(this->bAppend,BUGTRAQPROPNAME_APPEND);\r
105 \r
106         GetStringProps(sPropVal,BUGTRAQPROPNAME_LOGREGEX,false);\r
107 \r
108         sCheckRe = sPropVal;\r
109         if (sCheckRe.Find('\n')>=0)\r
110         {\r
111                 sBugIDRe = sCheckRe.Mid(sCheckRe.Find('\n')).Trim();\r
112                 sCheckRe = sCheckRe.Left(sCheckRe.Find('\n')).Trim();\r
113         }\r
114         if (!sCheckRe.IsEmpty())\r
115         {\r
116                 sCheckRe = sCheckRe.Trim();\r
117         }\r
118         return TRUE;\r
119 \r
120 \r
121 #if 0\r
122         BOOL bFoundBugtraqLabel = FALSE;\r
123         BOOL bFoundBugtraqMessage = FALSE;\r
124         BOOL bFoundBugtraqNumber = FALSE;\r
125         BOOL bFoundBugtraqLogRe = FALSE;\r
126         BOOL bFoundBugtraqURL = FALSE;\r
127         BOOL bFoundBugtraqWarnIssue = FALSE;\r
128         BOOL bFoundBugtraqAppend = FALSE;\r
129         BOOL bFoundLogWidth = FALSE;\r
130         BOOL bFoundLogTemplate = FALSE;\r
131         BOOL bFoundMinLogSize = FALSE;\r
132         BOOL bFoundMinLockMsgSize = FALSE;\r
133         BOOL bFoundFileListEnglish = FALSE;\r
134         BOOL bFoundProjectLanguage = FALSE;\r
135         BOOL bFoundUserFileProps = FALSE;\r
136         BOOL bFoundUserDirProps = FALSE;\r
137         BOOL bFoundWebViewRev = FALSE;\r
138         BOOL bFoundWebViewPathRev = FALSE;\r
139         BOOL bFoundAutoProps = FALSE;\r
140         BOOL bFoundLogSummary = FALSE;\r
141 \r
142         if (!path.IsDirectory())\r
143                 path = path.GetContainingDirectory();\r
144                 \r
145         for (;;)\r
146         {\r
147                 GitProperties props(path, GitRev::REV_WC, false);\r
148                 for (int i=0; i<props.GetCount(); ++i)\r
149                 {\r
150                         CString sPropName = props.GetItemName(i).c_str();\r
151                         CString sPropVal = CUnicodeUtils::GetUnicode(((char *)props.GetItemValue(i).c_str()));\r
152                         if ((!bFoundBugtraqLabel)&&(sPropName.Compare(BUGTRAQPROPNAME_LABEL)==0))\r
153                         {\r
154                                 sLabel = sPropVal;\r
155                                 bFoundBugtraqLabel = TRUE;\r
156                         }\r
157                         if ((!bFoundBugtraqMessage)&&(sPropName.Compare(BUGTRAQPROPNAME_MESSAGE)==0))\r
158                         {\r
159                                 sMessage = sPropVal;\r
160                                 bFoundBugtraqMessage = TRUE;\r
161                         }\r
162                         if ((!bFoundBugtraqNumber)&&(sPropName.Compare(BUGTRAQPROPNAME_NUMBER)==0))\r
163                         {\r
164                                 CString val;\r
165                                 val = sPropVal;\r
166                                 val = val.Trim(_T(" \n\r\t"));\r
167                                 if ((val.CompareNoCase(_T("false"))==0)||(val.CompareNoCase(_T("no"))==0))\r
168                                         bNumber = FALSE;\r
169                                 else\r
170                                         bNumber = TRUE;\r
171                                 bFoundBugtraqNumber = TRUE;\r
172                         }\r
173                         if ((!bFoundBugtraqLogRe)&&(sPropName.Compare(BUGTRAQPROPNAME_LOGREGEX)==0))\r
174                         {\r
175                                 sCheckRe = sPropVal;\r
176                                 if (sCheckRe.Find('\n')>=0)\r
177                                 {\r
178                                         sBugIDRe = sCheckRe.Mid(sCheckRe.Find('\n')).Trim();\r
179                                         sCheckRe = sCheckRe.Left(sCheckRe.Find('\n')).Trim();\r
180                                 }\r
181                                 if (!sCheckRe.IsEmpty())\r
182                                 {\r
183                                         sCheckRe = sCheckRe.Trim();\r
184                                 }\r
185                                 bFoundBugtraqLogRe = TRUE;\r
186                         }\r
187                         if ((!bFoundBugtraqURL)&&(sPropName.Compare(BUGTRAQPROPNAME_URL)==0))\r
188                         {\r
189                                 sUrl = sPropVal;\r
190                                 bFoundBugtraqURL = TRUE;\r
191                         }\r
192                         if ((!bFoundBugtraqWarnIssue)&&(sPropName.Compare(BUGTRAQPROPNAME_WARNIFNOISSUE)==0))\r
193                         {\r
194                                 CString val;\r
195                                 val = sPropVal;\r
196                                 val = val.Trim(_T(" \n\r\t"));\r
197                                 if ((val.CompareNoCase(_T("true"))==0)||(val.CompareNoCase(_T("yes"))==0))\r
198                                         bWarnIfNoIssue = TRUE;\r
199                                 else\r
200                                         bWarnIfNoIssue = FALSE;\r
201                                 bFoundBugtraqWarnIssue = TRUE;\r
202                         }\r
203                         if ((!bFoundBugtraqAppend)&&(sPropName.Compare(BUGTRAQPROPNAME_APPEND)==0))\r
204                         {\r
205                                 CString val;\r
206                                 val = sPropVal;\r
207                                 val = val.Trim(_T(" \n\r\t"));\r
208                                 if ((val.CompareNoCase(_T("true"))==0)||(val.CompareNoCase(_T("yes"))==0))\r
209                                         bAppend = TRUE;\r
210                                 else\r
211                                         bAppend = FALSE;\r
212                                 bFoundBugtraqAppend = TRUE;\r
213                         }\r
214                         if ((!bFoundLogWidth)&&(sPropName.Compare(PROJECTPROPNAME_LOGWIDTHLINE)==0))\r
215                         {\r
216                                 CString val;\r
217                                 val = sPropVal;\r
218                                 if (!val.IsEmpty())\r
219                                 {\r
220                                         nLogWidthMarker = _ttoi(val);\r
221                                 }\r
222                                 bFoundLogWidth = TRUE;\r
223                         }\r
224                         if ((!bFoundLogTemplate)&&(sPropName.Compare(PROJECTPROPNAME_LOGTEMPLATE)==0))\r
225                         {\r
226                                 sLogTemplate = sPropVal;\r
227                                 sLogTemplate.Replace(_T("\r"), _T(""));\r
228                                 sLogTemplate.Replace(_T("\n"), _T("\r\n"));\r
229                                 bFoundLogTemplate = TRUE;\r
230                         }\r
231                         if ((!bFoundMinLogSize)&&(sPropName.Compare(PROJECTPROPNAME_LOGMINSIZE)==0))\r
232                         {\r
233                                 CString val;\r
234                                 val = sPropVal;\r
235                                 if (!val.IsEmpty())\r
236                                 {\r
237                                         nMinLogSize = _ttoi(val);\r
238                                 }\r
239                                 bFoundMinLogSize = TRUE;\r
240                         }\r
241                         if ((!bFoundMinLockMsgSize)&&(sPropName.Compare(PROJECTPROPNAME_LOCKMSGMINSIZE)==0))\r
242                         {\r
243                                 CString val;\r
244                                 val = sPropVal;\r
245                                 if (!val.IsEmpty())\r
246                                 {\r
247                                         nMinLockMsgSize = _ttoi(val);\r
248                                 }\r
249                                 bFoundMinLockMsgSize = TRUE;\r
250                         }\r
251                         if ((!bFoundFileListEnglish)&&(sPropName.Compare(PROJECTPROPNAME_LOGFILELISTLANG)==0))\r
252                         {\r
253                                 CString val;\r
254                                 val = sPropVal;\r
255                                 val = val.Trim(_T(" \n\r\t"));\r
256                                 if ((val.CompareNoCase(_T("false"))==0)||(val.CompareNoCase(_T("no"))==0))\r
257                                         bFileListInEnglish = TRUE;\r
258                                 else\r
259                                         bFileListInEnglish = FALSE;\r
260                                 bFoundFileListEnglish = TRUE;\r
261                         }\r
262                         if ((!bFoundProjectLanguage)&&(sPropName.Compare(PROJECTPROPNAME_PROJECTLANGUAGE)==0))\r
263                         {\r
264                                 CString val;\r
265                                 val = sPropVal;\r
266                                 if (!val.IsEmpty())\r
267                                 {\r
268                                         LPTSTR strEnd;\r
269                                         lProjectLanguage = _tcstol(val, &strEnd, 0);\r
270                                 }\r
271                                 bFoundProjectLanguage = TRUE;\r
272                         }\r
273                         if ((!bFoundUserFileProps)&&(sPropName.Compare(PROJECTPROPNAME_USERFILEPROPERTY)==0))\r
274                         {\r
275                                 sFPPath = sPropVal;\r
276                                 sFPPath.Replace(_T("\r\n"), _T("\n"));\r
277                                 bFoundUserFileProps = TRUE;\r
278                         }\r
279                         if ((!bFoundUserDirProps)&&(sPropName.Compare(PROJECTPROPNAME_USERDIRPROPERTY)==0))\r
280                         {\r
281                                 sDPPath = sPropVal;\r
282                                 sDPPath.Replace(_T("\r\n"), _T("\n"));\r
283                                 bFoundUserDirProps = TRUE;\r
284                         }\r
285                         if ((!bFoundAutoProps)&&(sPropName.Compare(PROJECTPROPNAME_AUTOPROPS)==0))\r
286                         {\r
287                                 sAutoProps = sPropVal;\r
288                                 sAutoProps.Replace(_T("\r\n"), _T("\n"));\r
289                                 bFoundAutoProps = TRUE;\r
290                         }\r
291                         if ((!bFoundWebViewRev)&&(sPropName.Compare(PROJECTPROPNAME_WEBVIEWER_REV)==0))\r
292                         {\r
293                                 sWebViewerRev = sPropVal;\r
294                                 bFoundWebViewRev = TRUE;\r
295                         }\r
296                         if ((!bFoundWebViewPathRev)&&(sPropName.Compare(PROJECTPROPNAME_WEBVIEWER_PATHREV)==0))\r
297                         {\r
298                                 sWebViewerPathRev = sPropVal;\r
299                                 bFoundWebViewPathRev = TRUE;\r
300                         }\r
301                         if ((!bFoundLogSummary)&&(sPropName.Compare(PROJECTPROPNAME_LOGSUMMARY)==0))\r
302                         {\r
303                                 sLogSummaryRe = sPropVal;\r
304                                 bFoundLogSummary = TRUE;\r
305                         }\r
306                 }\r
307                 if (PathIsRoot(path.GetWinPath()))\r
308                         return FALSE;\r
309                 propsPath = path;\r
310                 path = path.GetContainingDirectory();\r
311                 if ((!path.HasAdminDir())||(path.IsEmpty()))\r
312                 {\r
313                         if (bFoundBugtraqLabel | bFoundBugtraqMessage | bFoundBugtraqNumber\r
314                                 | bFoundBugtraqURL | bFoundBugtraqWarnIssue | bFoundLogWidth\r
315                                 | bFoundLogTemplate | bFoundBugtraqLogRe | bFoundMinLockMsgSize\r
316                                 | bFoundUserFileProps | bFoundUserDirProps | bFoundAutoProps\r
317                                 | bFoundWebViewRev | bFoundWebViewPathRev | bFoundLogSummary)\r
318                         {\r
319                                 return TRUE;\r
320                         }\r
321                         propsPath.Reset();\r
322                         return FALSE;\r
323                 }\r
324         }\r
325 #endif\r
326         return FALSE;           //never reached\r
327 }\r
328 \r
329 CString ProjectProperties::GetBugIDFromLog(CString& msg)\r
330 {\r
331         CString sBugID;\r
332 \r
333         if (!sMessage.IsEmpty())\r
334         {\r
335                 CString sBugLine;\r
336                 CString sFirstPart;\r
337                 CString sLastPart;\r
338                 BOOL bTop = FALSE;\r
339                 if (sMessage.Find(_T("%BUGID%"))<0)\r
340                         return sBugID;\r
341                 sFirstPart = sMessage.Left(sMessage.Find(_T("%BUGID%")));\r
342                 sLastPart = sMessage.Mid(sMessage.Find(_T("%BUGID%"))+7);\r
343                 msg.TrimRight('\n');\r
344                 if (msg.ReverseFind('\n')>=0)\r
345                 {\r
346                         if (bAppend)\r
347                                 sBugLine = msg.Mid(msg.ReverseFind('\n')+1);\r
348                         else\r
349                         {\r
350                                 sBugLine = msg.Left(msg.Find('\n'));\r
351                                 bTop = TRUE;\r
352                         }\r
353                 }\r
354                 else\r
355                 {\r
356                         if (bNumber)\r
357                         {\r
358                                 // find out if the message consists only of numbers\r
359                                 bool bOnlyNumbers = true;\r
360                                 for (int i=0; i<msg.GetLength(); ++i)\r
361                                 {\r
362                                         if (!_istdigit(msg[i]))\r
363                                         {\r
364                                                 bOnlyNumbers = false;\r
365                                                 break;\r
366                                         }\r
367                                 }\r
368                                 if (bOnlyNumbers)\r
369                                         sBugLine = msg;\r
370                         }\r
371                         else\r
372                                 sBugLine = msg;\r
373                 }\r
374                 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)\r
375                         sBugLine.Empty();\r
376                 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
377                         sBugLine.Empty();\r
378                 if (sBugLine.IsEmpty())\r
379                 {\r
380                         if (msg.Find('\n')>=0)\r
381                                 sBugLine = msg.Left(msg.Find('\n'));\r
382                         if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)\r
383                                 sBugLine.Empty();\r
384                         if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
385                                 sBugLine.Empty();\r
386                         bTop = TRUE;\r
387                 }\r
388                 if (sBugLine.IsEmpty())\r
389                         return sBugID;\r
390                 sBugID = sBugLine.Mid(sFirstPart.GetLength(), sBugLine.GetLength() - sFirstPart.GetLength() - sLastPart.GetLength());\r
391                 if (bTop)\r
392                 {\r
393                         msg = msg.Mid(sBugLine.GetLength());\r
394                         msg.TrimLeft('\n');\r
395                 }\r
396                 else\r
397                 {\r
398                         msg = msg.Left(msg.GetLength()-sBugLine.GetLength());\r
399                         msg.TrimRight('\n');\r
400                 }\r
401         }\r
402         return sBugID;\r
403 }\r
404 \r
405 BOOL ProjectProperties::FindBugID(const CString& msg, CWnd * pWnd)\r
406 {\r
407         size_t offset1 = 0;\r
408         size_t offset2 = 0;\r
409         bool bFound = false;\r
410 \r
411         if (sUrl.IsEmpty())\r
412                 return FALSE;\r
413 \r
414         // first use the checkre string to find bug ID's in the message\r
415         if (!sCheckRe.IsEmpty())\r
416         {\r
417                 if (!sBugIDRe.IsEmpty())\r
418                 {\r
419 \r
420                         // match with two regex strings (without grouping!)\r
421                         try\r
422                         {\r
423                                 const tr1::wregex regCheck(sCheckRe);\r
424                                 const tr1::wregex regBugID(sBugIDRe);\r
425                                 const tr1::wsregex_iterator end;\r
426                                 wstring s = msg;\r
427                                 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
428                                 {\r
429                                         // (*it)[0] is the matched string\r
430                                         wstring matchedString = (*it)[0];\r
431                                         ptrdiff_t matchpos = it->position(0);\r
432                                         for (tr1::wsregex_iterator it2(matchedString.begin(), matchedString.end(), regBugID); it2 != end; ++it2)\r
433                                         {\r
434                                                 ATLTRACE(_T("matched id : %s\n"), (*it2)[0].str().c_str());\r
435                                                 ptrdiff_t matchposID = it2->position(0);\r
436                                                 CHARRANGE range = {matchpos+matchposID, matchpos+matchposID+(*it2)[0].str().size()};\r
437                                                 pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
438                                                 CHARFORMAT2 format;\r
439                                                 SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
440                                                 format.cbSize = sizeof(CHARFORMAT2);\r
441                                                 format.dwMask = CFM_LINK;\r
442                                                 format.dwEffects = CFE_LINK;\r
443                                                 pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
444                                                 bFound = true;\r
445                                         }\r
446                                 }\r
447                         }\r
448                         catch (exception) {}\r
449                 }\r
450                 else\r
451                 {\r
452                         try\r
453                         {\r
454                                 const tr1::wregex regCheck(sCheckRe);\r
455                                 const tr1::wsregex_iterator end;\r
456                                 wstring s = msg;\r
457                                 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
458                                 {\r
459                                         const tr1::wsmatch match = *it;\r
460                                         // we define group 1 as the whole issue text and\r
461                                         // group 2 as the bug ID\r
462                                         if (match.size() >= 2)\r
463                                         {\r
464                                                 ATLTRACE(_T("matched id : %s\n"), wstring(match[1]).c_str());\r
465                                                 CHARRANGE range = {match[1].first-s.begin(), match[1].second-s.begin()};\r
466                                                 pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
467                                                 CHARFORMAT2 format;\r
468                                                 SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
469                                                 format.cbSize = sizeof(CHARFORMAT2);\r
470                                                 format.dwMask = CFM_LINK;\r
471                                                 format.dwEffects = CFE_LINK;\r
472                                                 pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
473                                                 bFound = true;\r
474                                         }\r
475                                 }\r
476                         }\r
477                         catch (exception) {}\r
478                 }\r
479         }\r
480         else if ((!bFound)&&(!sMessage.IsEmpty()))\r
481         {\r
482                 CString sBugLine;\r
483                 CString sFirstPart;\r
484                 CString sLastPart;\r
485                 BOOL bTop = FALSE;\r
486                 if (sMessage.Find(_T("%BUGID%"))<0)\r
487                         return FALSE;\r
488                 sFirstPart = sMessage.Left(sMessage.Find(_T("%BUGID%")));\r
489                 sLastPart = sMessage.Mid(sMessage.Find(_T("%BUGID%"))+7);\r
490                 CString sMsg = msg;\r
491                 sMsg.TrimRight('\n');\r
492                 if (sMsg.ReverseFind('\n')>=0)\r
493                 {\r
494                         if (bAppend)\r
495                                 sBugLine = sMsg.Mid(sMsg.ReverseFind('\n')+1);\r
496                         else\r
497                         {\r
498                                 sBugLine = sMsg.Left(sMsg.Find('\n'));\r
499                                 bTop = TRUE;\r
500                         }\r
501                 }\r
502                 else\r
503                         sBugLine = sMsg;\r
504                 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)\r
505                         sBugLine.Empty();\r
506                 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
507                         sBugLine.Empty();\r
508                 if (sBugLine.IsEmpty())\r
509                 {\r
510                         if (sMsg.Find('\n')>=0)\r
511                                 sBugLine = sMsg.Left(sMsg.Find('\n'));\r
512                         if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)\r
513                                 sBugLine.Empty();\r
514                         if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
515                                 sBugLine.Empty();\r
516                         bTop = TRUE;\r
517                 }\r
518                 if (sBugLine.IsEmpty())\r
519                         return FALSE;\r
520                 CString sBugIDPart = sBugLine.Mid(sFirstPart.GetLength(), sBugLine.GetLength() - sFirstPart.GetLength() - sLastPart.GetLength());\r
521                 if (sBugIDPart.IsEmpty())\r
522                         return FALSE;\r
523                 //the bug id part can contain several bug id's, separated by commas\r
524                 if (!bTop)\r
525                         offset1 = sMsg.GetLength() - sBugLine.GetLength() + sFirstPart.GetLength();\r
526                 else\r
527                         offset1 = sFirstPart.GetLength();\r
528                 sBugIDPart.Trim(_T(","));\r
529                 while (sBugIDPart.Find(',')>=0)\r
530                 {\r
531                         offset2 = offset1 + sBugIDPart.Find(',');\r
532                         CHARRANGE range = {(LONG)offset1, (LONG)offset2};\r
533                         pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
534                         CHARFORMAT2 format;\r
535                         SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
536                         format.cbSize = sizeof(CHARFORMAT2);\r
537                         format.dwMask = CFM_LINK;\r
538                         format.dwEffects = CFE_LINK;\r
539                         pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
540                         sBugIDPart = sBugIDPart.Mid(sBugIDPart.Find(',')+1);\r
541                         offset1 = offset2 + 1;\r
542                 }\r
543                 offset2 = offset1 + sBugIDPart.GetLength();\r
544                 CHARRANGE range = {(LONG)offset1, (LONG)offset2};\r
545                 pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
546                 CHARFORMAT2 format;\r
547                 SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
548                 format.cbSize = sizeof(CHARFORMAT2);\r
549                 format.dwMask = CFM_LINK;\r
550                 format.dwEffects = CFE_LINK;\r
551                 pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
552                 return TRUE;\r
553         }\r
554         return FALSE;\r
555 }\r
556 \r
557 CString ProjectProperties::FindBugID(const CString& msg)\r
558 {\r
559         size_t offset1 = 0;\r
560         size_t offset2 = 0;\r
561         bool bFound = false;\r
562 \r
563         CString sRet;\r
564 \r
565         std::set<CString> bugIDs;\r
566 \r
567         // first use the checkre string to find bug ID's in the message\r
568         if (!sCheckRe.IsEmpty())\r
569         {\r
570                 if (!sBugIDRe.IsEmpty())\r
571                 {\r
572                         // match with two regex strings (without grouping!)\r
573                         try\r
574                         {\r
575                                 const tr1::wregex regCheck(sCheckRe);\r
576                                 const tr1::wregex regBugID(sBugIDRe);\r
577                                 const tr1::wsregex_iterator end;\r
578                                 wstring s = msg;\r
579                                 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
580                                 {\r
581                                         // (*it)[0] is the matched string\r
582                                         wstring matchedString = (*it)[0];\r
583                                         for (tr1::wsregex_iterator it2(matchedString.begin(), matchedString.end(), regBugID); it2 != end; ++it2)\r
584                                         {\r
585                                                 ATLTRACE(_T("matched id : %s\n"), (*it2)[0].str().c_str());\r
586                                                 bugIDs.insert(CString((*it2)[0].str().c_str()));\r
587                                         }\r
588                                 }\r
589                         }\r
590                         catch (exception) {}\r
591                 }\r
592                 else\r
593                 {\r
594                         try\r
595                         {\r
596                                 const tr1::wregex regCheck(sCheckRe);\r
597                                 const tr1::wsregex_iterator end;\r
598                                 wstring s = msg;\r
599                                 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
600                                 {\r
601                                         const tr1::wsmatch match = *it;\r
602                                         // we define group 1 as the whole issue text and\r
603                                         // group 2 as the bug ID\r
604                                         if (match.size() >= 2)\r
605                                         {\r
606                                                 ATLTRACE(_T("matched id : %s\n"), wstring(match[1]).c_str());\r
607                                                 bugIDs.insert(CString(wstring(match[1]).c_str()));\r
608                                         }\r
609                                 }\r
610                         }\r
611                         catch (exception) {}\r
612                 }\r
613         }\r
614         else if ((!bFound)&&(!sMessage.IsEmpty()))\r
615         {\r
616                 CString sBugLine;\r
617                 CString sFirstPart;\r
618                 CString sLastPart;\r
619                 BOOL bTop = FALSE;\r
620                 if (sMessage.Find(_T("%BUGID%"))<0)\r
621                         goto finish;\r
622                 sFirstPart = sMessage.Left(sMessage.Find(_T("%BUGID%")));\r
623                 sLastPart = sMessage.Mid(sMessage.Find(_T("%BUGID%"))+7);\r
624                 CString sMsg = msg;\r
625                 sMsg.TrimRight('\n');\r
626                 if (sMsg.ReverseFind('\n')>=0)\r
627                 {\r
628                         if (bAppend)\r
629                                 sBugLine = sMsg.Mid(sMsg.ReverseFind('\n')+1);\r
630                         else\r
631                         {\r
632                                 sBugLine = sMsg.Left(sMsg.Find('\n'));\r
633                                 bTop = TRUE;\r
634                         }\r
635                 }\r
636                 else\r
637                         sBugLine = sMsg;\r
638                 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)\r
639                         sBugLine.Empty();\r
640                 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
641                         sBugLine.Empty();\r
642                 if (sBugLine.IsEmpty())\r
643                 {\r
644                         if (sMsg.Find('\n')>=0)\r
645                                 sBugLine = sMsg.Left(sMsg.Find('\n'));\r
646                         if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)\r
647                                 sBugLine.Empty();\r
648                         if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
649                                 sBugLine.Empty();\r
650                         bTop = TRUE;\r
651                 }\r
652                 if (sBugLine.IsEmpty())\r
653                         goto finish;\r
654                 CString sBugIDPart = sBugLine.Mid(sFirstPart.GetLength(), sBugLine.GetLength() - sFirstPart.GetLength() - sLastPart.GetLength());\r
655                 if (sBugIDPart.IsEmpty())\r
656                         goto finish;\r
657                 //the bug id part can contain several bug id's, separated by commas\r
658                 if (!bTop)\r
659                         offset1 = sMsg.GetLength() - sBugLine.GetLength() + sFirstPart.GetLength();\r
660                 else\r
661                         offset1 = sFirstPart.GetLength();\r
662                 sBugIDPart.Trim(_T(","));\r
663                 while (sBugIDPart.Find(',')>=0)\r
664                 {\r
665                         offset2 = offset1 + sBugIDPart.Find(',');\r
666                         CHARRANGE range = {(LONG)offset1, (LONG)offset2};\r
667                         bugIDs.insert(msg.Mid(range.cpMin, range.cpMax-range.cpMin));\r
668                         sBugIDPart = sBugIDPart.Mid(sBugIDPart.Find(',')+1);\r
669                         offset1 = offset2 + 1;\r
670                 }\r
671                 offset2 = offset1 + sBugIDPart.GetLength();\r
672                 CHARRANGE range = {(LONG)offset1, (LONG)offset2};\r
673                 bugIDs.insert(msg.Mid(range.cpMin, range.cpMax-range.cpMin));\r
674         }\r
675 finish:\r
676         for (std::set<CString>::iterator it = bugIDs.begin(); it != bugIDs.end(); ++it)\r
677         {\r
678                 sRet += *it;\r
679                 sRet += _T(" ");\r
680         }\r
681         sRet.Trim();\r
682         return sRet;\r
683 }\r
684 \r
685 CString ProjectProperties::GetBugIDUrl(const CString& sBugID)\r
686 {\r
687         CString ret;\r
688         if (sUrl.IsEmpty())\r
689                 return ret;\r
690         if (!sMessage.IsEmpty() || !sCheckRe.IsEmpty())\r
691         {\r
692                 ret = sUrl;\r
693                 ret.Replace(_T("%BUGID%"), sBugID);\r
694         }\r
695         return ret;\r
696 }\r
697 \r
698 BOOL ProjectProperties::CheckBugID(const CString& sID)\r
699 {\r
700         if (!sCheckRe.IsEmpty()&&(!bNumber)&&!sID.IsEmpty())\r
701         {\r
702                 CString sBugID = sID;\r
703                 sBugID.Replace(_T(", "), _T(","));\r
704                 sBugID.Replace(_T(" ,"), _T(","));\r
705                 CString sMsg = sMessage;\r
706                 sMsg.Replace(_T("%BUGID%"), sBugID);\r
707                 return HasBugID(sMsg);\r
708         }\r
709         if (bNumber)\r
710         {\r
711                 // check if the revision actually _is_ a number\r
712                 // or a list of numbers separated by colons\r
713                 TCHAR c = 0;\r
714                 int len = sID.GetLength();\r
715                 for (int i=0; i<len; ++i)\r
716                 {\r
717                         c = sID.GetAt(i);\r
718                         if ((c < '0')&&(c != ','))\r
719                         {\r
720                                 return FALSE;\r
721                         }\r
722                         if (c > '9')\r
723                                 return FALSE;\r
724                 }\r
725         }\r
726         return TRUE;\r
727 }\r
728 \r
729 BOOL ProjectProperties::HasBugID(const CString& sMessage)\r
730 {\r
731         if (!sCheckRe.IsEmpty())\r
732         {\r
733                 try\r
734                 {\r
735                         tr1::wregex rx(sCheckRe);\r
736                         return tr1::regex_search((LPCTSTR)sMessage, rx);\r
737                 }\r
738                 catch (exception) {}\r
739         }\r
740         return FALSE;\r
741 }\r
742 #if 0\r
743 void ProjectProperties::InsertAutoProps(Git_config_t *cfg)\r
744 {\r
745         // every line is an autoprop\r
746         CString sPropsData = sAutoProps;\r
747         bool bEnableAutoProps = false;\r
748         while (!sPropsData.IsEmpty())\r
749         {\r
750                 int pos = sPropsData.Find('\n');\r
751                 CString sLine = pos >= 0 ? sPropsData.Left(pos) : sPropsData;\r
752                 sLine.Trim();\r
753                 if (!sLine.IsEmpty())\r
754                 {\r
755                         // format is '*.something = property=value;property=value;....'\r
756                         // find first '=' char\r
757                         int equalpos = sLine.Find('=');\r
758                         if ((equalpos >= 0)&&(sLine[0] != '#'))\r
759                         {\r
760                                 CString key = sLine.Left(equalpos);\r
761                                 CString value = sLine.Mid(equalpos);\r
762                                 key.Trim(_T(" ="));\r
763                                 value.Trim(_T(" ="));\r
764                                 Git_config_set(cfg, Git_CONFIG_SECTION_AUTO_PROPS, (LPCSTR)CUnicodeUtils::GetUTF8(key), (LPCSTR)CUnicodeUtils::GetUTF8(value));\r
765                                 bEnableAutoProps = true;\r
766                         }\r
767                 }\r
768                 if (pos >= 0)\r
769                         sPropsData = sPropsData.Mid(pos).Trim();\r
770                 else\r
771                         sPropsData.Empty();\r
772         }\r
773         if (bEnableAutoProps)\r
774                 Git_config_set(cfg, Git_CONFIG_SECTION_MISCELLANY, Git_CONFIG_OPTION_ENABLE_AUTO_PROPS, "yes");\r
775 }\r
776 #endif\r
777 \r
778 bool ProjectProperties::AddAutoProps(const CTGitPath& path)\r
779 {\r
780         if (!path.IsDirectory())\r
781                 return true;    // no error, but nothing to do\r
782 \r
783         bool bRet = true;\r
784 \r
785         char buf[1024] = {0};\r
786 #if 0\r
787         GitProperties props(path, GitRev::REV_WC, false);\r
788         if (!sLabel.IsEmpty())\r
789                 bRet = props.Add(BUGTRAQPROPNAME_LABEL, WideToMultibyte((LPCTSTR)sLabel)) && bRet;\r
790         if (!sMessage.IsEmpty())\r
791                 bRet = props.Add(BUGTRAQPROPNAME_MESSAGE, WideToMultibyte((LPCTSTR)sMessage)) && bRet;\r
792         if (!bNumber)\r
793                 bRet = props.Add(BUGTRAQPROPNAME_NUMBER, "false") && bRet;\r
794         if (!sCheckRe.IsEmpty())\r
795                 bRet = props.Add(BUGTRAQPROPNAME_LOGREGEX, WideToMultibyte((LPCTSTR)(sCheckRe + _T("\n") + sBugIDRe))) && bRet;\r
796         if (!sUrl.IsEmpty())\r
797                 bRet = props.Add(BUGTRAQPROPNAME_URL, WideToMultibyte((LPCTSTR)sUrl)) && bRet;\r
798         if (bWarnIfNoIssue)\r
799                 bRet = props.Add(BUGTRAQPROPNAME_WARNIFNOISSUE, "true") && bRet;\r
800         if (!bAppend)\r
801                 bRet = props.Add(BUGTRAQPROPNAME_APPEND, "false") && bRet;\r
802         if (nLogWidthMarker)\r
803         {\r
804                 sprintf_s(buf, sizeof(buf), "%ld", nLogWidthMarker);\r
805                 bRet = props.Add(PROJECTPROPNAME_LOGWIDTHLINE, buf) && bRet;\r
806         }\r
807         if (!sLogTemplate.IsEmpty())\r
808                 bRet = props.Add(PROJECTPROPNAME_LOGTEMPLATE, WideToMultibyte((LPCTSTR)sLogTemplate)) && bRet;\r
809         if (nMinLogSize)\r
810         {\r
811                 sprintf_s(buf, sizeof(buf), "%ld", nMinLogSize);\r
812                 bRet = props.Add(PROJECTPROPNAME_LOGMINSIZE, buf) && bRet;\r
813         }\r
814         if (nMinLockMsgSize)\r
815         {\r
816                 sprintf_s(buf, sizeof(buf), "%ld", nMinLockMsgSize);\r
817                 bRet = props.Add(PROJECTPROPNAME_LOCKMSGMINSIZE, buf) && bRet;\r
818         }\r
819         if (!bFileListInEnglish)\r
820                 bRet = props.Add(PROJECTPROPNAME_LOGFILELISTLANG, "false") && bRet;\r
821         if (lProjectLanguage)\r
822         {\r
823                 sprintf_s(buf, sizeof(buf), "%ld", lProjectLanguage);\r
824                 bRet = props.Add(PROJECTPROPNAME_PROJECTLANGUAGE, buf) && bRet;\r
825         }\r
826         if (!sFPPath.IsEmpty())\r
827                 bRet = props.Add(PROJECTPROPNAME_USERFILEPROPERTY, WideToMultibyte((LPCTSTR)sFPPath)) && bRet;\r
828         if (!sDPPath.IsEmpty())\r
829                 bRet = props.Add(PROJECTPROPNAME_USERDIRPROPERTY, WideToMultibyte((LPCTSTR)sDPPath)) && bRet;\r
830         if (!sWebViewerRev.IsEmpty())\r
831                 bRet = props.Add(PROJECTPROPNAME_WEBVIEWER_REV, WideToMultibyte((LPCTSTR)sWebViewerRev)) && bRet;\r
832         if (!sWebViewerPathRev.IsEmpty())\r
833                 bRet = props.Add(PROJECTPROPNAME_WEBVIEWER_PATHREV, WideToMultibyte((LPCTSTR)sWebViewerPathRev)) && bRet;\r
834         if (!sAutoProps.IsEmpty())\r
835                 bRet = props.Add(PROJECTPROPNAME_AUTOPROPS, WideToMultibyte((LPCTSTR)sAutoProps)) && bRet;\r
836 #endif\r
837         return bRet;\r
838 }\r
839 \r
840 CString ProjectProperties::GetLogSummary(const CString& sMessage)\r
841 {\r
842         CString sRet;\r
843 \r
844         if (!sLogSummaryRe.IsEmpty())\r
845         {\r
846                 try\r
847                 {\r
848                         const tr1::wregex regSum(sLogSummaryRe);\r
849                         const tr1::wsregex_iterator end;\r
850                         wstring s = sMessage;\r
851                         for (tr1::wsregex_iterator it(s.begin(), s.end(), regSum); it != end; ++it)\r
852                         {\r
853                                 const tr1::wsmatch match = *it;\r
854                                 // we define the first group as the summary text\r
855                                 if ((*it).size() >= 1)\r
856                                 {\r
857                                         ATLTRACE(_T("matched summary : %s\n"), wstring(match[0]).c_str());\r
858                                         sRet += (CString(wstring(match[1]).c_str()));\r
859                                 }\r
860                         }\r
861                 }\r
862                 catch (exception) {}\r
863         }\r
864         sRet.Trim();\r
865 \r
866         return sRet;\r
867 }\r
868 \r
869 #ifdef DEBUG\r
870 static class PropTest\r
871 {\r
872 public:\r
873         PropTest()\r
874         {\r
875                 CString msg = _T("this is a test logmessage: issue 222\nIssue #456, #678, 901  #456");\r
876                 CString sUrl = _T("http://tortoiseGit.tigris.org/issues/show_bug.cgi?id=%BUGID%");\r
877                 CString sCheckRe = _T("[Ii]ssue #?(\\d+)(,? ?#?(\\d+))+");\r
878                 CString sBugIDRe = _T("(\\d+)");\r
879                 ProjectProperties props;\r
880                 props.sCheckRe = _T("PAF-[0-9]+");\r
881                 props.sUrl = _T("http://tortoiseGit.tigris.org/issues/show_bug.cgi?id=%BUGID%");\r
882                 CString sRet = props.FindBugID(_T("This is a test for PAF-88"));\r
883                 ATLASSERT(sRet.IsEmpty());\r
884                 props.sCheckRe = _T("[Ii]ssue #?(\\d+)");\r
885                 sRet = props.FindBugID(_T("Testing issue #99"));\r
886                 sRet.Trim();\r
887                 ATLASSERT(sRet.Compare(_T("99"))==0);\r
888                 props.sCheckRe = _T("[Ii]ssues?:?(\\s*(,|and)?\\s*#\\d+)+");\r
889                 props.sBugIDRe = _T("(\\d+)");\r
890                 props.sUrl = _T("http://tortoiseGit.tigris.org/issues/show_bug.cgi?id=%BUGID%");\r
891                 sRet = props.FindBugID(_T("This is a test for Issue #7463,#666"));\r
892                 ATLASSERT(props.HasBugID(_T("This is a test for Issue #7463,#666")));\r
893                 ATLASSERT(!props.HasBugID(_T("This is a test for Issue 7463,666")));\r
894                 sRet.Trim();\r
895                 ATLASSERT(sRet.Compare(_T("666 7463"))==0);\r
896                 props.sCheckRe = _T("^\\[(\\d+)\\].*");\r
897                 props.sUrl = _T("http://tortoiseGit.tigris.org/issues/show_bug.cgi?id=%BUGID%");\r
898                 sRet = props.FindBugID(_T("[000815] some stupid programming error fixed"));\r
899                 sRet.Trim();\r
900                 ATLASSERT(sRet.Compare(_T("000815"))==0);\r
901                 props.sCheckRe = _T("\\[\\[(\\d+)\\]\\]\\]");\r
902                 props.sUrl = _T("http://tortoiseGit.tigris.org/issues/show_bug.cgi?id=%BUGID%");\r
903                 sRet = props.FindBugID(_T("test test [[000815]]] some stupid programming error fixed"));\r
904                 sRet.Trim();\r
905                 ATLASSERT(sRet.Compare(_T("000815"))==0);\r
906                 ATLASSERT(props.HasBugID(_T("test test [[000815]]] some stupid programming error fixed")));\r
907                 ATLASSERT(!props.HasBugID(_T("test test [000815]] some stupid programming error fixed")));\r
908                 props.sLogSummaryRe = _T("\\[SUMMARY\\]:(.*)");\r
909                 ATLASSERT(props.GetLogSummary(_T("[SUMMARY]: this is my summary")).Compare(_T("this is my summary"))==0);\r
910         }\r
911 } PropTest;\r
912 #endif\r
913 \r
914 \r
915 \r