OSDN Git Service

4510fdf9cf680ee4ce786c81d03c6d0b452e8fa4
[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 \r
27 using namespace std;\r
28 \r
29 \r
30 ProjectProperties::ProjectProperties(void)\r
31 {\r
32         bNumber = TRUE;\r
33         bWarnIfNoIssue = FALSE;\r
34         nLogWidthMarker = 0;\r
35         nMinLogSize = 0;\r
36         nMinLockMsgSize = 0;\r
37         bFileListInEnglish = TRUE;\r
38         bAppend = TRUE;\r
39         lProjectLanguage = 0;\r
40 }\r
41 \r
42 ProjectProperties::~ProjectProperties(void)\r
43 {\r
44 }\r
45 \r
46 \r
47 BOOL ProjectProperties::ReadPropsPathList(const CTGitPathList& pathList)\r
48 {\r
49         for(int nPath = 0; nPath < pathList.GetCount(); nPath++)\r
50         {\r
51                 if (ReadProps(pathList[nPath]))\r
52                 {\r
53                         return TRUE;\r
54                 }\r
55         }\r
56         return FALSE;\r
57 }\r
58 \r
59 BOOL ProjectProperties::ReadProps(CTGitPath path)\r
60 {\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
80 \r
81         if (!path.IsDirectory())\r
82                 path = path.GetContainingDirectory();\r
83 #if 0           \r
84         for (;;)\r
85         {\r
86                 GitProperties props(path, GitRev::REV_WC, false);\r
87                 for (int i=0; i<props.GetCount(); ++i)\r
88                 {\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
92                         {\r
93                                 sLabel = sPropVal;\r
94                                 bFoundBugtraqLabel = TRUE;\r
95                         }\r
96                         if ((!bFoundBugtraqMessage)&&(sPropName.Compare(BUGTRAQPROPNAME_MESSAGE)==0))\r
97                         {\r
98                                 sMessage = sPropVal;\r
99                                 bFoundBugtraqMessage = TRUE;\r
100                         }\r
101                         if ((!bFoundBugtraqNumber)&&(sPropName.Compare(BUGTRAQPROPNAME_NUMBER)==0))\r
102                         {\r
103                                 CString val;\r
104                                 val = sPropVal;\r
105                                 val = val.Trim(_T(" \n\r\t"));\r
106                                 if ((val.CompareNoCase(_T("false"))==0)||(val.CompareNoCase(_T("no"))==0))\r
107                                         bNumber = FALSE;\r
108                                 else\r
109                                         bNumber = TRUE;\r
110                                 bFoundBugtraqNumber = TRUE;\r
111                         }\r
112                         if ((!bFoundBugtraqLogRe)&&(sPropName.Compare(BUGTRAQPROPNAME_LOGREGEX)==0))\r
113                         {\r
114                                 sCheckRe = sPropVal;\r
115                                 if (sCheckRe.Find('\n')>=0)\r
116                                 {\r
117                                         sBugIDRe = sCheckRe.Mid(sCheckRe.Find('\n')).Trim();\r
118                                         sCheckRe = sCheckRe.Left(sCheckRe.Find('\n')).Trim();\r
119                                 }\r
120                                 if (!sCheckRe.IsEmpty())\r
121                                 {\r
122                                         sCheckRe = sCheckRe.Trim();\r
123                                 }\r
124                                 bFoundBugtraqLogRe = TRUE;\r
125                         }\r
126                         if ((!bFoundBugtraqURL)&&(sPropName.Compare(BUGTRAQPROPNAME_URL)==0))\r
127                         {\r
128                                 sUrl = sPropVal;\r
129                                 bFoundBugtraqURL = TRUE;\r
130                         }\r
131                         if ((!bFoundBugtraqWarnIssue)&&(sPropName.Compare(BUGTRAQPROPNAME_WARNIFNOISSUE)==0))\r
132                         {\r
133                                 CString val;\r
134                                 val = sPropVal;\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
138                                 else\r
139                                         bWarnIfNoIssue = FALSE;\r
140                                 bFoundBugtraqWarnIssue = TRUE;\r
141                         }\r
142                         if ((!bFoundBugtraqAppend)&&(sPropName.Compare(BUGTRAQPROPNAME_APPEND)==0))\r
143                         {\r
144                                 CString val;\r
145                                 val = sPropVal;\r
146                                 val = val.Trim(_T(" \n\r\t"));\r
147                                 if ((val.CompareNoCase(_T("true"))==0)||(val.CompareNoCase(_T("yes"))==0))\r
148                                         bAppend = TRUE;\r
149                                 else\r
150                                         bAppend = FALSE;\r
151                                 bFoundBugtraqAppend = TRUE;\r
152                         }\r
153                         if ((!bFoundLogWidth)&&(sPropName.Compare(PROJECTPROPNAME_LOGWIDTHLINE)==0))\r
154                         {\r
155                                 CString val;\r
156                                 val = sPropVal;\r
157                                 if (!val.IsEmpty())\r
158                                 {\r
159                                         nLogWidthMarker = _ttoi(val);\r
160                                 }\r
161                                 bFoundLogWidth = TRUE;\r
162                         }\r
163                         if ((!bFoundLogTemplate)&&(sPropName.Compare(PROJECTPROPNAME_LOGTEMPLATE)==0))\r
164                         {\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
169                         }\r
170                         if ((!bFoundMinLogSize)&&(sPropName.Compare(PROJECTPROPNAME_LOGMINSIZE)==0))\r
171                         {\r
172                                 CString val;\r
173                                 val = sPropVal;\r
174                                 if (!val.IsEmpty())\r
175                                 {\r
176                                         nMinLogSize = _ttoi(val);\r
177                                 }\r
178                                 bFoundMinLogSize = TRUE;\r
179                         }\r
180                         if ((!bFoundMinLockMsgSize)&&(sPropName.Compare(PROJECTPROPNAME_LOCKMSGMINSIZE)==0))\r
181                         {\r
182                                 CString val;\r
183                                 val = sPropVal;\r
184                                 if (!val.IsEmpty())\r
185                                 {\r
186                                         nMinLockMsgSize = _ttoi(val);\r
187                                 }\r
188                                 bFoundMinLockMsgSize = TRUE;\r
189                         }\r
190                         if ((!bFoundFileListEnglish)&&(sPropName.Compare(PROJECTPROPNAME_LOGFILELISTLANG)==0))\r
191                         {\r
192                                 CString val;\r
193                                 val = sPropVal;\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
197                                 else\r
198                                         bFileListInEnglish = FALSE;\r
199                                 bFoundFileListEnglish = TRUE;\r
200                         }\r
201                         if ((!bFoundProjectLanguage)&&(sPropName.Compare(PROJECTPROPNAME_PROJECTLANGUAGE)==0))\r
202                         {\r
203                                 CString val;\r
204                                 val = sPropVal;\r
205                                 if (!val.IsEmpty())\r
206                                 {\r
207                                         LPTSTR strEnd;\r
208                                         lProjectLanguage = _tcstol(val, &strEnd, 0);\r
209                                 }\r
210                                 bFoundProjectLanguage = TRUE;\r
211                         }\r
212                         if ((!bFoundUserFileProps)&&(sPropName.Compare(PROJECTPROPNAME_USERFILEPROPERTY)==0))\r
213                         {\r
214                                 sFPPath = sPropVal;\r
215                                 sFPPath.Replace(_T("\r\n"), _T("\n"));\r
216                                 bFoundUserFileProps = TRUE;\r
217                         }\r
218                         if ((!bFoundUserDirProps)&&(sPropName.Compare(PROJECTPROPNAME_USERDIRPROPERTY)==0))\r
219                         {\r
220                                 sDPPath = sPropVal;\r
221                                 sDPPath.Replace(_T("\r\n"), _T("\n"));\r
222                                 bFoundUserDirProps = TRUE;\r
223                         }\r
224                         if ((!bFoundAutoProps)&&(sPropName.Compare(PROJECTPROPNAME_AUTOPROPS)==0))\r
225                         {\r
226                                 sAutoProps = sPropVal;\r
227                                 sAutoProps.Replace(_T("\r\n"), _T("\n"));\r
228                                 bFoundAutoProps = TRUE;\r
229                         }\r
230                         if ((!bFoundWebViewRev)&&(sPropName.Compare(PROJECTPROPNAME_WEBVIEWER_REV)==0))\r
231                         {\r
232                                 sWebViewerRev = sPropVal;\r
233                                 bFoundWebViewRev = TRUE;\r
234                         }\r
235                         if ((!bFoundWebViewPathRev)&&(sPropName.Compare(PROJECTPROPNAME_WEBVIEWER_PATHREV)==0))\r
236                         {\r
237                                 sWebViewerPathRev = sPropVal;\r
238                                 bFoundWebViewPathRev = TRUE;\r
239                         }\r
240                         if ((!bFoundLogSummary)&&(sPropName.Compare(PROJECTPROPNAME_LOGSUMMARY)==0))\r
241                         {\r
242                                 sLogSummaryRe = sPropVal;\r
243                                 bFoundLogSummary = TRUE;\r
244                         }\r
245                 }\r
246                 if (PathIsRoot(path.GetWinPath()))\r
247                         return FALSE;\r
248                 propsPath = path;\r
249                 path = path.GetContainingDirectory();\r
250                 if ((!path.HasAdminDir())||(path.IsEmpty()))\r
251                 {\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
257                         {\r
258                                 return TRUE;\r
259                         }\r
260                         propsPath.Reset();\r
261                         return FALSE;\r
262                 }\r
263         }\r
264 #endif\r
265         return FALSE;           //never reached\r
266 }\r
267 \r
268 CString ProjectProperties::GetBugIDFromLog(CString& msg)\r
269 {\r
270         CString sBugID;\r
271 \r
272         if (!sMessage.IsEmpty())\r
273         {\r
274                 CString sBugLine;\r
275                 CString sFirstPart;\r
276                 CString sLastPart;\r
277                 BOOL bTop = FALSE;\r
278                 if (sMessage.Find(_T("%BUGID%"))<0)\r
279                         return sBugID;\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
284                 {\r
285                         if (bAppend)\r
286                                 sBugLine = msg.Mid(msg.ReverseFind('\n')+1);\r
287                         else\r
288                         {\r
289                                 sBugLine = msg.Left(msg.Find('\n'));\r
290                                 bTop = TRUE;\r
291                         }\r
292                 }\r
293                 else\r
294                 {\r
295                         if (bNumber)\r
296                         {\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
300                                 {\r
301                                         if (!_istdigit(msg[i]))\r
302                                         {\r
303                                                 bOnlyNumbers = false;\r
304                                                 break;\r
305                                         }\r
306                                 }\r
307                                 if (bOnlyNumbers)\r
308                                         sBugLine = msg;\r
309                         }\r
310                         else\r
311                                 sBugLine = msg;\r
312                 }\r
313                 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)\r
314                         sBugLine.Empty();\r
315                 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
316                         sBugLine.Empty();\r
317                 if (sBugLine.IsEmpty())\r
318                 {\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
322                                 sBugLine.Empty();\r
323                         if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
324                                 sBugLine.Empty();\r
325                         bTop = TRUE;\r
326                 }\r
327                 if (sBugLine.IsEmpty())\r
328                         return sBugID;\r
329                 sBugID = sBugLine.Mid(sFirstPart.GetLength(), sBugLine.GetLength() - sFirstPart.GetLength() - sLastPart.GetLength());\r
330                 if (bTop)\r
331                 {\r
332                         msg = msg.Mid(sBugLine.GetLength());\r
333                         msg.TrimLeft('\n');\r
334                 }\r
335                 else\r
336                 {\r
337                         msg = msg.Left(msg.GetLength()-sBugLine.GetLength());\r
338                         msg.TrimRight('\n');\r
339                 }\r
340         }\r
341         return sBugID;\r
342 }\r
343 \r
344 BOOL ProjectProperties::FindBugID(const CString& msg, CWnd * pWnd)\r
345 {\r
346         size_t offset1 = 0;\r
347         size_t offset2 = 0;\r
348         bool bFound = false;\r
349 \r
350         if (sUrl.IsEmpty())\r
351                 return FALSE;\r
352 \r
353         // first use the checkre string to find bug ID's in the message\r
354         if (!sCheckRe.IsEmpty())\r
355         {\r
356                 if (!sBugIDRe.IsEmpty())\r
357                 {\r
358 \r
359                         // match with two regex strings (without grouping!)\r
360                         try\r
361                         {\r
362                                 const tr1::wregex regCheck(sCheckRe);\r
363                                 const tr1::wregex regBugID(sBugIDRe);\r
364                                 const tr1::wsregex_iterator end;\r
365                                 wstring s = msg;\r
366                                 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
367                                 {\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
372                                         {\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
383                                                 bFound = true;\r
384                                         }\r
385                                 }\r
386                         }\r
387                         catch (exception) {}\r
388                 }\r
389                 else\r
390                 {\r
391                         try\r
392                         {\r
393                                 const tr1::wregex regCheck(sCheckRe);\r
394                                 const tr1::wsregex_iterator end;\r
395                                 wstring s = msg;\r
396                                 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
397                                 {\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
402                                         {\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
412                                                 bFound = true;\r
413                                         }\r
414                                 }\r
415                         }\r
416                         catch (exception) {}\r
417                 }\r
418         }\r
419         else if ((!bFound)&&(!sMessage.IsEmpty()))\r
420         {\r
421                 CString sBugLine;\r
422                 CString sFirstPart;\r
423                 CString sLastPart;\r
424                 BOOL bTop = FALSE;\r
425                 if (sMessage.Find(_T("%BUGID%"))<0)\r
426                         return FALSE;\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
432                 {\r
433                         if (bAppend)\r
434                                 sBugLine = sMsg.Mid(sMsg.ReverseFind('\n')+1);\r
435                         else\r
436                         {\r
437                                 sBugLine = sMsg.Left(sMsg.Find('\n'));\r
438                                 bTop = TRUE;\r
439                         }\r
440                 }\r
441                 else\r
442                         sBugLine = sMsg;\r
443                 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)\r
444                         sBugLine.Empty();\r
445                 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
446                         sBugLine.Empty();\r
447                 if (sBugLine.IsEmpty())\r
448                 {\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
452                                 sBugLine.Empty();\r
453                         if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
454                                 sBugLine.Empty();\r
455                         bTop = TRUE;\r
456                 }\r
457                 if (sBugLine.IsEmpty())\r
458                         return FALSE;\r
459                 CString sBugIDPart = sBugLine.Mid(sFirstPart.GetLength(), sBugLine.GetLength() - sFirstPart.GetLength() - sLastPart.GetLength());\r
460                 if (sBugIDPart.IsEmpty())\r
461                         return FALSE;\r
462                 //the bug id part can contain several bug id's, separated by commas\r
463                 if (!bTop)\r
464                         offset1 = sMsg.GetLength() - sBugLine.GetLength() + sFirstPart.GetLength();\r
465                 else\r
466                         offset1 = sFirstPart.GetLength();\r
467                 sBugIDPart.Trim(_T(","));\r
468                 while (sBugIDPart.Find(',')>=0)\r
469                 {\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
481                 }\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
491                 return TRUE;\r
492         }\r
493         return FALSE;\r
494 }\r
495 \r
496 CString ProjectProperties::FindBugID(const CString& msg)\r
497 {\r
498         size_t offset1 = 0;\r
499         size_t offset2 = 0;\r
500         bool bFound = false;\r
501 \r
502         CString sRet;\r
503 \r
504         std::set<CString> bugIDs;\r
505 \r
506         // first use the checkre string to find bug ID's in the message\r
507         if (!sCheckRe.IsEmpty())\r
508         {\r
509                 if (!sBugIDRe.IsEmpty())\r
510                 {\r
511                         // match with two regex strings (without grouping!)\r
512                         try\r
513                         {\r
514                                 const tr1::wregex regCheck(sCheckRe);\r
515                                 const tr1::wregex regBugID(sBugIDRe);\r
516                                 const tr1::wsregex_iterator end;\r
517                                 wstring s = msg;\r
518                                 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
519                                 {\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
523                                         {\r
524                                                 ATLTRACE(_T("matched id : %s\n"), (*it2)[0].str().c_str());\r
525                                                 bugIDs.insert(CString((*it2)[0].str().c_str()));\r
526                                         }\r
527                                 }\r
528                         }\r
529                         catch (exception) {}\r
530                 }\r
531                 else\r
532                 {\r
533                         try\r
534                         {\r
535                                 const tr1::wregex regCheck(sCheckRe);\r
536                                 const tr1::wsregex_iterator end;\r
537                                 wstring s = msg;\r
538                                 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
539                                 {\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
544                                         {\r
545                                                 ATLTRACE(_T("matched id : %s\n"), wstring(match[1]).c_str());\r
546                                                 bugIDs.insert(CString(wstring(match[1]).c_str()));\r
547                                         }\r
548                                 }\r
549                         }\r
550                         catch (exception) {}\r
551                 }\r
552         }\r
553         else if ((!bFound)&&(!sMessage.IsEmpty()))\r
554         {\r
555                 CString sBugLine;\r
556                 CString sFirstPart;\r
557                 CString sLastPart;\r
558                 BOOL bTop = FALSE;\r
559                 if (sMessage.Find(_T("%BUGID%"))<0)\r
560                         goto finish;\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
566                 {\r
567                         if (bAppend)\r
568                                 sBugLine = sMsg.Mid(sMsg.ReverseFind('\n')+1);\r
569                         else\r
570                         {\r
571                                 sBugLine = sMsg.Left(sMsg.Find('\n'));\r
572                                 bTop = TRUE;\r
573                         }\r
574                 }\r
575                 else\r
576                         sBugLine = sMsg;\r
577                 if (sBugLine.Left(sFirstPart.GetLength()).Compare(sFirstPart)!=0)\r
578                         sBugLine.Empty();\r
579                 if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
580                         sBugLine.Empty();\r
581                 if (sBugLine.IsEmpty())\r
582                 {\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
586                                 sBugLine.Empty();\r
587                         if (sBugLine.Right(sLastPart.GetLength()).Compare(sLastPart)!=0)\r
588                                 sBugLine.Empty();\r
589                         bTop = TRUE;\r
590                 }\r
591                 if (sBugLine.IsEmpty())\r
592                         goto finish;\r
593                 CString sBugIDPart = sBugLine.Mid(sFirstPart.GetLength(), sBugLine.GetLength() - sFirstPart.GetLength() - sLastPart.GetLength());\r
594                 if (sBugIDPart.IsEmpty())\r
595                         goto finish;\r
596                 //the bug id part can contain several bug id's, separated by commas\r
597                 if (!bTop)\r
598                         offset1 = sMsg.GetLength() - sBugLine.GetLength() + sFirstPart.GetLength();\r
599                 else\r
600                         offset1 = sFirstPart.GetLength();\r
601                 sBugIDPart.Trim(_T(","));\r
602                 while (sBugIDPart.Find(',')>=0)\r
603                 {\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
609                 }\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
613         }\r
614 finish:\r
615         for (std::set<CString>::iterator it = bugIDs.begin(); it != bugIDs.end(); ++it)\r
616         {\r
617                 sRet += *it;\r
618                 sRet += _T(" ");\r
619         }\r
620         sRet.Trim();\r
621         return sRet;\r
622 }\r
623 \r
624 CString ProjectProperties::GetBugIDUrl(const CString& sBugID)\r
625 {\r
626         CString ret;\r
627         if (sUrl.IsEmpty())\r
628                 return ret;\r
629         if (!sMessage.IsEmpty() || !sCheckRe.IsEmpty())\r
630         {\r
631                 ret = sUrl;\r
632                 ret.Replace(_T("%BUGID%"), sBugID);\r
633         }\r
634         return ret;\r
635 }\r
636 \r
637 BOOL ProjectProperties::CheckBugID(const CString& sID)\r
638 {\r
639         if (!sCheckRe.IsEmpty()&&(!bNumber)&&!sID.IsEmpty())\r
640         {\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
647         }\r
648         if (bNumber)\r
649         {\r
650                 // check if the revision actually _is_ a number\r
651                 // or a list of numbers separated by colons\r
652                 TCHAR c = 0;\r
653                 int len = sID.GetLength();\r
654                 for (int i=0; i<len; ++i)\r
655                 {\r
656                         c = sID.GetAt(i);\r
657                         if ((c < '0')&&(c != ','))\r
658                         {\r
659                                 return FALSE;\r
660                         }\r
661                         if (c > '9')\r
662                                 return FALSE;\r
663                 }\r
664         }\r
665         return TRUE;\r
666 }\r
667 \r
668 BOOL ProjectProperties::HasBugID(const CString& sMessage)\r
669 {\r
670         if (!sCheckRe.IsEmpty())\r
671         {\r
672                 try\r
673                 {\r
674                         tr1::wregex rx(sCheckRe);\r
675                         return tr1::regex_search((LPCTSTR)sMessage, rx);\r
676                 }\r
677                 catch (exception) {}\r
678         }\r
679         return FALSE;\r
680 }\r
681 #if 0\r
682 void ProjectProperties::InsertAutoProps(Git_config_t *cfg)\r
683 {\r
684         // every line is an autoprop\r
685         CString sPropsData = sAutoProps;\r
686         bool bEnableAutoProps = false;\r
687         while (!sPropsData.IsEmpty())\r
688         {\r
689                 int pos = sPropsData.Find('\n');\r
690                 CString sLine = pos >= 0 ? sPropsData.Left(pos) : sPropsData;\r
691                 sLine.Trim();\r
692                 if (!sLine.IsEmpty())\r
693                 {\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
698                         {\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
705                         }\r
706                 }\r
707                 if (pos >= 0)\r
708                         sPropsData = sPropsData.Mid(pos).Trim();\r
709                 else\r
710                         sPropsData.Empty();\r
711         }\r
712         if (bEnableAutoProps)\r
713                 Git_config_set(cfg, Git_CONFIG_SECTION_MISCELLANY, Git_CONFIG_OPTION_ENABLE_AUTO_PROPS, "yes");\r
714 }\r
715 #endif\r
716 \r
717 bool ProjectProperties::AddAutoProps(const CTGitPath& path)\r
718 {\r
719         if (!path.IsDirectory())\r
720                 return true;    // no error, but nothing to do\r
721 \r
722         bool bRet = true;\r
723 \r
724         char buf[1024] = {0};\r
725 #if 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
731         if (!bNumber)\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
739         if (!bAppend)\r
740                 bRet = props.Add(BUGTRAQPROPNAME_APPEND, "false") && bRet;\r
741         if (nLogWidthMarker)\r
742         {\r
743                 sprintf_s(buf, sizeof(buf), "%ld", nLogWidthMarker);\r
744                 bRet = props.Add(PROJECTPROPNAME_LOGWIDTHLINE, buf) && bRet;\r
745         }\r
746         if (!sLogTemplate.IsEmpty())\r
747                 bRet = props.Add(PROJECTPROPNAME_LOGTEMPLATE, WideToMultibyte((LPCTSTR)sLogTemplate)) && bRet;\r
748         if (nMinLogSize)\r
749         {\r
750                 sprintf_s(buf, sizeof(buf), "%ld", nMinLogSize);\r
751                 bRet = props.Add(PROJECTPROPNAME_LOGMINSIZE, buf) && bRet;\r
752         }\r
753         if (nMinLockMsgSize)\r
754         {\r
755                 sprintf_s(buf, sizeof(buf), "%ld", nMinLockMsgSize);\r
756                 bRet = props.Add(PROJECTPROPNAME_LOCKMSGMINSIZE, buf) && bRet;\r
757         }\r
758         if (!bFileListInEnglish)\r
759                 bRet = props.Add(PROJECTPROPNAME_LOGFILELISTLANG, "false") && bRet;\r
760         if (lProjectLanguage)\r
761         {\r
762                 sprintf_s(buf, sizeof(buf), "%ld", lProjectLanguage);\r
763                 bRet = props.Add(PROJECTPROPNAME_PROJECTLANGUAGE, buf) && bRet;\r
764         }\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
775 #endif\r
776         return bRet;\r
777 }\r
778 \r
779 CString ProjectProperties::GetLogSummary(const CString& sMessage)\r
780 {\r
781         CString sRet;\r
782 \r
783         if (!sLogSummaryRe.IsEmpty())\r
784         {\r
785                 try\r
786                 {\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
791                         {\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
795                                 {\r
796                                         ATLTRACE(_T("matched summary : %s\n"), wstring(match[0]).c_str());\r
797                                         sRet += (CString(wstring(match[1]).c_str()));\r
798                                 }\r
799                         }\r
800                 }\r
801                 catch (exception) {}\r
802         }\r
803         sRet.Trim();\r
804 \r
805         return sRet;\r
806 }\r
807 \r
808 #ifdef DEBUG\r
809 static class PropTest\r
810 {\r
811 public:\r
812         PropTest()\r
813         {\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
825                 sRet.Trim();\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
833                 sRet.Trim();\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
838                 sRet.Trim();\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
843                 sRet.Trim();\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
849         }\r
850 } PropTest;\r
851 #endif\r
852 \r
853 \r
854 \r