OSDN Git Service

Fixed issue #122: Garbage text in "Git Command Progress" / suspected buffer overflow
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / ProgressDlg.cpp
1 // ProgressDlg.cpp : implementation file\r
2 //\r
3 \r
4 #include "stdafx.h"\r
5 #include "TortoiseProc.h"\r
6 #include "ProgressDlg.h"\r
7 #include "Git.h"\r
8 #include "atlconv.h"\r
9 // CProgressDlg dialog\r
10 \r
11 IMPLEMENT_DYNAMIC(CProgressDlg, CResizableStandAloneDialog)\r
12 \r
13 CProgressDlg::CProgressDlg(CWnd* pParent /*=NULL*/)\r
14         : CResizableStandAloneDialog(CProgressDlg::IDD, pParent), m_bShowCommand(true), m_bAutoCloseOnSuccess(false), m_bAbort(false), m_bDone(false)\r
15 {\r
16         m_pThread = NULL;\r
17         m_bAltAbortPress=false;\r
18         m_bBufferAll=false;\r
19 }\r
20 \r
21 CProgressDlg::~CProgressDlg()\r
22 {\r
23         if(m_pThread != NULL)\r
24         {\r
25                 delete m_pThread;\r
26         }\r
27 }\r
28 \r
29 void CProgressDlg::DoDataExchange(CDataExchange* pDX)\r
30 {\r
31         CDialog::DoDataExchange(pDX);\r
32         DDX_Control(pDX, IDC_CURRENT, this->m_CurrentWork);\r
33         DDX_Control(pDX, IDC_TITLE_ANIMATE, this->m_Animate);\r
34         DDX_Control(pDX, IDC_RUN_PROGRESS, this->m_Progress);\r
35         DDX_Control(pDX, IDC_LOG, this->m_Log);\r
36 }\r
37 \r
38 \r
39 BEGIN_MESSAGE_MAP(CProgressDlg, CResizableStandAloneDialog)\r
40         ON_MESSAGE(MSG_PROGRESSDLG_UPDATE_UI, OnProgressUpdateUI)\r
41         ON_BN_CLICKED(IDOK, &CProgressDlg::OnBnClickedOk)\r
42         ON_BN_CLICKED(IDC_PROGRESS_BUTTON1,&CProgressDlg::OnBnClickedButton1)\r
43 END_MESSAGE_MAP()\r
44 \r
45 BOOL CProgressDlg::OnInitDialog()\r
46 {\r
47         CResizableStandAloneDialog::OnInitDialog();\r
48 \r
49         AddAnchor(IDC_TITLE_ANIMATE, TOP_LEFT, TOP_RIGHT);\r
50         AddAnchor(IDC_RUN_PROGRESS, TOP_LEFT,TOP_RIGHT);\r
51         AddAnchor(IDC_LOG, TOP_LEFT,BOTTOM_RIGHT);\r
52 \r
53         AddAnchor(IDOK,BOTTOM_RIGHT);\r
54         AddAnchor(IDCANCEL,BOTTOM_RIGHT);\r
55         AddAnchor(IDC_PROGRESS_BUTTON1,BOTTOM_RIGHT);\r
56 \r
57         this->GetDlgItem(IDC_PROGRESS_BUTTON1)->ShowWindow(SW_HIDE);\r
58         m_Animate.Open(IDR_DOWNLOAD);\r
59         \r
60         CString InitialText;\r
61         if ( !m_PreText.IsEmpty() )\r
62         {\r
63                 InitialText = m_PreText + _T("\r\n");\r
64         }\r
65 #if 0\r
66         if (m_bShowCommand && (!m_GitCmd.IsEmpty() ))\r
67         {\r
68                 InitialText += m_GitCmd+_T("\r\n\r\n");\r
69         }\r
70 #endif\r
71         m_Log.SetWindowTextW(InitialText);\r
72         m_CurrentWork.SetWindowTextW(_T(""));\r
73 \r
74         m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);\r
75         if (m_pThread==NULL)\r
76         {\r
77 //              ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));\r
78         }\r
79         else\r
80         {\r
81                 m_pThread->m_bAutoDelete = FALSE;\r
82                 m_pThread->ResumeThread();\r
83         }\r
84 \r
85         if(!m_Title.IsEmpty())\r
86                 this->SetWindowText(m_Title);\r
87         return TRUE;\r
88 }\r
89 \r
90 UINT CProgressDlg::ProgressThreadEntry(LPVOID pVoid)\r
91 {\r
92         return ((CProgressDlg*)pVoid)->ProgressThread();\r
93 }\r
94 \r
95 //static function, Share with SyncDialog\r
96 UINT CProgressDlg::RunCmdList(CWnd *pWnd,std::vector<CString> &cmdlist,bool bShowCommand,CString *pfilename,bool *bAbort,std::vector<TCHAR>*pdata)\r
97 {\r
98         UINT ret=0;\r
99 \r
100         PROCESS_INFORMATION pi;\r
101         HANDLE hRead;\r
102 \r
103         pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_START,0);\r
104 \r
105         if(pdata)\r
106                 pdata->clear();\r
107                  \r
108         for(int i=0;i<cmdlist.size();i++)\r
109         {\r
110                 if(cmdlist[i].IsEmpty())\r
111                         continue;\r
112 \r
113                 if (bShowCommand)\r
114                 {\r
115                         CString str;\r
116                         str+= cmdlist[i]+_T("\n\n");\r
117                         for(int j=0;j<str.GetLength();j++)\r
118                         {\r
119                                 if(pdata)\r
120                                         pdata->push_back(str[j]);\r
121                                 else\r
122                                         pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,str[j]);\r
123                         }\r
124                         if(pdata)\r
125                                 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,0);\r
126                 }\r
127 \r
128                 g_Git.RunAsync(cmdlist[i],&pi, &hRead,pfilename);\r
129 \r
130                 DWORD readnumber;\r
131                 char buffer[2];\r
132                 CString output;\r
133                 while(ReadFile(hRead,buffer,1,&readnumber,NULL))\r
134                 {\r
135                         buffer[readnumber]=0;\r
136                         \r
137                         if(pdata)\r
138                         {\r
139                                 pdata->push_back((TCHAR)buffer[0]);\r
140 \r
141                                 if(buffer[0] == '\r' || buffer[0] == '\n')\r
142                                         pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,0);\r
143                         }else\r
144                                 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,buffer[0]);\r
145                 }\r
146         \r
147                 CloseHandle(pi.hThread);\r
148 \r
149                 WaitForSingleObject(pi.hProcess, INFINITE);\r
150                 \r
151                 DWORD status=0;\r
152                 if(!GetExitCodeProcess(pi.hProcess,&status) || *bAbort)\r
153                 {\r
154                         CloseHandle(pi.hProcess);\r
155 \r
156                         CloseHandle(hRead);\r
157 \r
158                         pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_FAILED,0);\r
159                         return GIT_ERROR_GET_EXIT_CODE;\r
160                 }\r
161                 ret |= status;\r
162         }\r
163 \r
164         CloseHandle(pi.hProcess);\r
165 \r
166         CloseHandle(hRead);\r
167 \r
168         pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_END,0);\r
169 \r
170         return ret;\r
171 \r
172 }\r
173 \r
174 UINT CProgressDlg::ProgressThread()\r
175 {\r
176         \r
177         m_GitCmdList.push_back(m_GitCmd);\r
178 \r
179         CString *pfilename;\r
180 \r
181         if(m_LogFile.IsEmpty())\r
182                 pfilename=NULL;\r
183         else\r
184                 pfilename=&m_LogFile;   \r
185 \r
186         m_GitStatus = RunCmdList(this,m_GitCmdList,m_bShowCommand,pfilename,&m_bAbort,&this->m_Databuf);;\r
187         return 0;\r
188 }\r
189 \r
190 LRESULT CProgressDlg::OnProgressUpdateUI(WPARAM wParam,LPARAM lParam)\r
191 {\r
192         if(wParam == MSG_PROGRESSDLG_START)\r
193         {\r
194                 m_BufStart = 0 ;\r
195                 m_Animate.Play(0,-1,-1);\r
196                 this->DialogEnableWindow(IDOK,FALSE);\r
197         }\r
198         if(wParam == MSG_PROGRESSDLG_END || wParam == MSG_PROGRESSDLG_FAILED)\r
199         {\r
200                 if(m_bBufferAll)\r
201                 {\r
202                         m_Databuf.push_back(0);\r
203                         InsertCRLF();\r
204                         m_Log.SetWindowText(&m_Databuf[0]);\r
205                 }\r
206                 m_BufStart=0;\r
207                 this->m_Databuf.clear();\r
208 \r
209                 m_bDone = true;\r
210                 m_Animate.Stop();\r
211                 m_Progress.SetPos(100);\r
212                 this->DialogEnableWindow(IDOK,TRUE);\r
213 \r
214                 if(wParam == MSG_PROGRESSDLG_END && m_GitStatus == 0)\r
215                 {\r
216                         if(m_bAutoCloseOnSuccess)\r
217                                 EndDialog(IDOK);\r
218 \r
219                         if(!m_changeAbortButtonOnSuccessTo.IsEmpty())\r
220                         {\r
221                                 GetDlgItem(IDC_PROGRESS_BUTTON1)->SetWindowText(m_changeAbortButtonOnSuccessTo);\r
222                                 GetDlgItem(IDC_PROGRESS_BUTTON1)->ShowWindow(SW_SHOW);\r
223                                 GetDlgItem(IDCANCEL)->ShowWindow(SW_HIDE);\r
224                                 //Set default button is "close" rather than "push"\r
225                                 this->SendMessage(WM_NEXTDLGCTL, (WPARAM)GetDlgItem(IDOK)->m_hWnd, TRUE);\r
226                         }\r
227                         else\r
228                                 DialogEnableWindow(IDCANCEL, FALSE);\r
229                 }\r
230                 else\r
231                         DialogEnableWindow(IDCANCEL, FALSE);\r
232         }\r
233 \r
234         if(!m_bBufferAll)\r
235         {\r
236                 if(lParam == 0)\r
237                 {\r
238                         for(int i=this->m_BufStart;i<this->m_Databuf.size();i++)\r
239                         {\r
240                                 ParserCmdOutput(this->m_Databuf[m_BufStart]);\r
241                                 m_BufStart++;\r
242                         }\r
243                 }else\r
244                         ParserCmdOutput((TCHAR)lParam);\r
245         }\r
246         return 0;\r
247 }\r
248 \r
249 //static function, Share with SyncDialog\r
250 int CProgressDlg::FindPercentage(CString &log)\r
251 {\r
252         int s1=log.Find(_T('%'));\r
253         if(s1<0)\r
254                 return -1;\r
255 \r
256         int s2=s1-1;\r
257         for(int i=s1-1;i>=0;i--)\r
258         {\r
259                 if(log[i]>=_T('0') && log[i]<=_T('9'))\r
260                         s2=i;\r
261                 else\r
262                         break;\r
263         }\r
264         return _ttol(log.Mid(s2,s1-s2));\r
265 }\r
266 \r
267 void CProgressDlg::ParserCmdOutput(TCHAR ch)\r
268 {\r
269         TRACE(_T("%c"),ch);\r
270         if( ch == _T('\r') || ch == _T('\n'))\r
271         {\r
272                 TRACE(_T("End Char %s \r\n"),ch==_T('\r')?_T("lf"):_T(""));\r
273                 TRACE(_T("End Char %s \r\n"),ch==_T('\n')?_T("cr"):_T(""));\r
274 \r
275                 CString text;\r
276                 m_Log.GetWindowTextW(text);\r
277                 if(ch == _T('\r'))\r
278                 {\r
279                         RemoveLastLine(text);\r
280                 }\r
281                 text+=_T("\r\n")+m_LogText;\r
282                 m_Log.SetWindowTextW(text);\r
283                 \r
284                 m_Log.LineScroll(m_Log.GetLineCount());\r
285 \r
286                 int s1=m_LogText.Find(_T(':'));\r
287                 int s2=m_LogText.Find(_T('%'));\r
288                 if(s1>0 && s2>0)\r
289                 {\r
290                         this->m_CurrentWork.SetWindowTextW(m_LogText.Left(s1));\r
291                         int pos=FindPercentage(m_LogText);\r
292                         TRACE(_T("Pos %d\r\n"),pos);\r
293                         if(pos>0)\r
294                                 this->m_Progress.SetPos(pos);\r
295                 }\r
296 \r
297                 m_LogText=_T("");\r
298 \r
299         }else\r
300         {\r
301                 m_LogText+=ch;\r
302         }\r
303 \r
304 }\r
305 void CProgressDlg::RemoveLastLine(CString &str)\r
306 {\r
307         int start;\r
308         start=str.ReverseFind(_T('\n'));\r
309         if(start>0)\r
310                 str=str.Left(start);\r
311         return;\r
312 }\r
313 // CProgressDlg message handlers\r
314 \r
315 void CProgressDlg::OnBnClickedOk()\r
316 {\r
317         // TODO: Add your control notification handler code here\r
318         m_Log.GetWindowText(this->m_LogText);\r
319         OnOK();\r
320 }\r
321 \r
322 void CProgressDlg::OnBnClickedButton1()\r
323 {\r
324         this->EndDialog(IDC_PROGRESS_BUTTON1);\r
325         \r
326 }\r
327 void CProgressDlg::OnCancel()\r
328 {\r
329         if(m_bDone)\r
330         {\r
331                 CResizableStandAloneDialog::OnCancel();\r
332                 return;\r
333         }\r
334         \r
335         m_bAbort = true;\r
336 }\r
337 \r
338 void CProgressDlg::InsertCRLF()\r
339 {\r
340         for(int i=0;i<m_Databuf.size();i++)\r
341         {\r
342                 if(m_Databuf[i]==_T('\n'))\r
343                 {\r
344                         if(i==0 || m_Databuf[i-1]!= _T('\r'))\r
345                         {\r
346                                 m_Databuf.insert(m_Databuf.begin()+i,_T('\r'));\r
347                                 i++;\r
348                         }\r
349                 }\r
350         }\r
351 }