Mercurial > hg > easyhg
comparison hgrunner.cpp @ 113:5fc7b4fc77a8
* Better error handling/reporting; some futile changes to termios handling; avoid weirdly stretching panned view in panner
author | Chris Cannam |
---|---|
date | Fri, 26 Nov 2010 21:04:40 +0000 |
parents | 151209bc5bd6 |
children | bb2d2eecdd60 |
comparison
equal
deleted
inserted
replaced
112:4bd17f36d059 | 113:5fc7b4fc77a8 |
---|---|
32 #include <stdio.h> | 32 #include <stdio.h> |
33 #include <stdlib.h> | 33 #include <stdlib.h> |
34 | 34 |
35 #ifndef Q_OS_WIN32 | 35 #ifndef Q_OS_WIN32 |
36 #include <unistd.h> | 36 #include <unistd.h> |
37 #include <termios.h> | |
37 #include <fcntl.h> | 38 #include <fcntl.h> |
38 #endif | 39 #endif |
39 | 40 |
40 HgRunner::HgRunner(QWidget * parent): QProgressBar(parent) | 41 HgRunner::HgRunner(QWidget * parent): QProgressBar(parent) |
41 { | 42 { |
42 m_proc = new QProcess(this); | 43 m_proc = 0; |
43 | |
44 QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); | |
45 env.insert("LANG", "en_US.utf8"); | |
46 env.insert("LC_ALL", "en_US.utf8"); | |
47 env.insert("HGPLAIN", "1"); | |
48 m_proc->setProcessEnvironment(env); | |
49 | 44 |
50 setTextVisible(false); | 45 setTextVisible(false); |
51 setVisible(false); | 46 setVisible(false); |
52 m_isRunning = false; | 47 m_isRunning = false; |
53 | |
54 connect(m_proc, SIGNAL(started()), this, SLOT(started())); | |
55 connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)), | |
56 this, SLOT(finished(int, QProcess::ExitStatus))); | |
57 connect(m_proc, SIGNAL(readyReadStandardOutput()), | |
58 this, SLOT(dataReadyStdout())); | |
59 connect(m_proc, SIGNAL(readyReadStandardError()), | |
60 this, SLOT(dataReadyStderr())); | |
61 } | 48 } |
62 | 49 |
63 HgRunner::~HgRunner() | 50 HgRunner::~HgRunner() |
64 { | 51 { |
65 closeTerminal(); | 52 closeTerminal(); |
122 m_realm = realm; | 109 m_realm = realm; |
123 } | 110 } |
124 | 111 |
125 void HgRunner::getUsername() | 112 void HgRunner::getUsername() |
126 { | 113 { |
127 if (m_procInput) { | 114 if (m_ptyFile) { |
128 bool ok = false; | 115 bool ok = false; |
129 QString prompt = tr("User name:"); | 116 QString prompt = tr("User name:"); |
130 if (m_realm != "") { | 117 if (m_realm != "") { |
131 prompt = tr("User name for \"%1\":").arg(m_realm); | 118 prompt = tr("User name for \"%1\":").arg(m_realm); |
132 } | 119 } |
133 QString pwd = QInputDialog::getText | 120 QString pwd = QInputDialog::getText |
134 (qobject_cast<QWidget *>(parent()), | 121 (qobject_cast<QWidget *>(parent()), |
135 tr("Enter user name"), prompt, | 122 tr("Enter user name"), prompt, |
136 QLineEdit::Normal, QString(), &ok); | 123 QLineEdit::Normal, QString(), &ok); |
137 if (ok) { | 124 if (ok) { |
138 m_procInput->write(QString("%1\n").arg(pwd).toUtf8()); | 125 m_ptyFile->write(QString("%1\n").arg(pwd).toUtf8()); |
139 m_procInput->flush(); | 126 m_ptyFile->flush(); |
140 return; | 127 return; |
141 } else { | 128 } else { |
142 DEBUG << "HgRunner::getUsername: user cancelled" << endl; | 129 DEBUG << "HgRunner::getUsername: user cancelled" << endl; |
143 killCurrentCommand(); | 130 killCurrentCommand(); |
144 return; | 131 return; |
149 killCurrentCommand(); | 136 killCurrentCommand(); |
150 } | 137 } |
151 | 138 |
152 void HgRunner::getPassword() | 139 void HgRunner::getPassword() |
153 { | 140 { |
154 if (m_procInput) { | 141 if (m_ptyFile) { |
155 bool ok = false; | 142 bool ok = false; |
156 QString prompt = tr("Password:"); | 143 QString prompt = tr("Password:"); |
157 if (m_userName != "") { | 144 if (m_userName != "") { |
158 if (m_realm != "") { | 145 if (m_realm != "") { |
159 prompt = tr("Password for \"%1\" at \"%2\":") | 146 prompt = tr("Password for \"%1\" at \"%2\":") |
166 QString pwd = QInputDialog::getText | 153 QString pwd = QInputDialog::getText |
167 (qobject_cast<QWidget *>(parent()), | 154 (qobject_cast<QWidget *>(parent()), |
168 tr("Enter password"), prompt, | 155 tr("Enter password"), prompt, |
169 QLineEdit::Password, QString(), &ok); | 156 QLineEdit::Password, QString(), &ok); |
170 if (ok) { | 157 if (ok) { |
171 m_procInput->write(QString("%1\n").arg(pwd).toUtf8()); | 158 m_ptyFile->write(QString("%1\n").arg(pwd).toUtf8()); |
172 m_procInput->flush(); | 159 m_ptyFile->flush(); |
173 return; | 160 return; |
174 } else { | 161 } else { |
175 DEBUG << "HgRunner::getPassword: user cancelled" << endl; | 162 DEBUG << "HgRunner::getPassword: user cancelled" << endl; |
176 killCurrentCommand(); | 163 killCurrentCommand(); |
177 return; | 164 return; |
180 // user cancelled or something went wrong | 167 // user cancelled or something went wrong |
181 DEBUG << "HgRunner::getPassword: something went wrong" << endl; | 168 DEBUG << "HgRunner::getPassword: something went wrong" << endl; |
182 killCurrentCommand(); | 169 killCurrentCommand(); |
183 } | 170 } |
184 | 171 |
185 void HgRunner::checkPrompts(QString chunk) | 172 bool HgRunner::checkPrompts(QString chunk) |
186 { | 173 { |
187 //DEBUG << "checkPrompts: " << chunk << endl; | 174 //DEBUG << "checkPrompts: " << chunk << endl; |
188 | 175 |
189 QString text = chunk.trimmed(); | 176 QString text = chunk.trimmed(); |
190 QString lower = text.toLower(); | 177 QString lower = text.toLower(); |
191 if (lower.endsWith("password:")) { | 178 if (lower.endsWith("password:")) { |
192 getPassword(); | 179 getPassword(); |
193 return; | 180 return true; |
194 } | 181 } |
195 if (lower.endsWith("user:")) { | 182 if (lower.endsWith("user:")) { |
196 getUsername(); | 183 getUsername(); |
197 return; | 184 return true; |
198 } | 185 } |
199 QRegExp userRe("\\buser:\\s*([^\\s]+)"); | 186 QRegExp userRe("\\buser:\\s*([^\\s]+)"); |
200 if (userRe.indexIn(text) >= 0) { | 187 if (userRe.indexIn(text) >= 0) { |
201 noteUsername(userRe.cap(1)); | 188 noteUsername(userRe.cap(1)); |
202 } | 189 } |
203 QRegExp realmRe("\\brealmr:\\s*([^\\s]+)"); | 190 QRegExp realmRe("\\brealmr:\\s*([^\\s]+)"); |
204 if (realmRe.indexIn(text) >= 0) { | 191 if (realmRe.indexIn(text) >= 0) { |
205 noteRealm(realmRe.cap(1)); | 192 noteRealm(realmRe.cap(1)); |
206 } | 193 } |
194 return false; | |
207 } | 195 } |
208 | 196 |
209 void HgRunner::dataReadyStdout() | 197 void HgRunner::dataReadyStdout() |
210 { | 198 { |
211 DEBUG << "dataReadyStdout" << endl; | 199 DEBUG << "dataReadyStdout" << endl; |
212 QString chunk = QString::fromUtf8(m_proc->readAllStandardOutput()); | 200 QString chunk = QString::fromUtf8(m_proc->readAllStandardOutput()); |
213 m_stdout += chunk; | 201 if (!checkPrompts(chunk)) { |
214 checkPrompts(chunk); | 202 m_stdout += chunk; |
203 } | |
215 } | 204 } |
216 | 205 |
217 void HgRunner::dataReadyStderr() | 206 void HgRunner::dataReadyStderr() |
218 { | 207 { |
219 DEBUG << "dataReadyStderr" << endl; | 208 DEBUG << "dataReadyStderr" << endl; |
220 QString chunk = QString::fromUtf8(m_proc->readAllStandardError()); | 209 QString chunk = QString::fromUtf8(m_proc->readAllStandardError()); |
221 m_stderr += chunk; | 210 DEBUG << chunk; |
222 checkPrompts(chunk); | 211 if (!checkPrompts(chunk)) { |
212 m_stderr += chunk; | |
213 } | |
214 } | |
215 | |
216 void HgRunner::dataReadyPty() | |
217 { | |
218 DEBUG << "dataReadyPty" << endl; | |
219 QString chunk = QString::fromUtf8(m_ptyFile->readAll()); | |
220 DEBUG << "chunk of " << chunk.length() << " chars" << endl; | |
221 if (!checkPrompts(chunk)) { | |
222 m_stdout += chunk; | |
223 } | |
223 } | 224 } |
224 | 225 |
225 void HgRunner::finished(int procExitCode, QProcess::ExitStatus procExitStatus) | 226 void HgRunner::finished(int procExitCode, QProcess::ExitStatus procExitStatus) |
226 { | 227 { |
227 // Save the current action and reset m_currentAction before we | 228 // Save the current action and reset m_currentAction before we |
233 | 234 |
234 m_isRunning = false; | 235 m_isRunning = false; |
235 m_currentAction = HgAction(); | 236 m_currentAction = HgAction(); |
236 | 237 |
237 closeProcInput(); | 238 closeProcInput(); |
239 delete m_proc; | |
240 m_proc = 0; | |
238 | 241 |
239 if (completedAction.action == ACT_NONE) { | 242 if (completedAction.action == ACT_NONE) { |
240 DEBUG << "HgRunner::finished: WARNING: completed action is ACT_NONE" << endl; | 243 DEBUG << "HgRunner::finished: WARNING: completed action is ACT_NONE" << endl; |
241 } | |
242 | |
243 if (procExitCode == 0 && procExitStatus == QProcess::NormalExit) { | |
244 DEBUG << "HgRunner::finished: Command completed successfully" << endl; | |
245 emit commandCompleted(completedAction, m_stdout); | |
246 } else { | 244 } else { |
247 DEBUG << "HgRunner::finished: Command failed, stderr follows" << endl; | 245 if (procExitCode == 0 && procExitStatus == QProcess::NormalExit) { |
248 DEBUG << m_stderr << endl; | 246 DEBUG << "HgRunner::finished: Command completed successfully" |
249 emit commandFailed(completedAction, m_stderr); | 247 << endl; |
248 emit commandCompleted(completedAction, m_stdout); | |
249 } else { | |
250 DEBUG << "HgRunner::finished: Command failed, exit code " | |
251 << procExitCode << ", exit status " << procExitStatus | |
252 << ", stderr follows" << endl; | |
253 DEBUG << m_stderr << endl; | |
254 emit commandFailed(completedAction, m_stderr); | |
255 } | |
250 } | 256 } |
251 | 257 |
252 checkQueue(); | 258 checkQueue(); |
253 } | 259 } |
254 | 260 |
255 void HgRunner::killCurrentCommand() | 261 void HgRunner::killCurrentCommand() |
256 { | 262 { |
257 if (m_isRunning) { | 263 if (m_isRunning) { |
258 m_proc -> kill(); | 264 m_currentAction.action = ACT_NONE; // so that we don't bother to notify |
265 m_proc->kill(); | |
259 } | 266 } |
260 } | 267 } |
261 | 268 |
262 void HgRunner::checkQueue() | 269 void HgRunner::checkQueue() |
263 { | 270 { |
304 m_stdout.clear(); | 311 m_stdout.clear(); |
305 m_stderr.clear(); | 312 m_stderr.clear(); |
306 m_realm = ""; | 313 m_realm = ""; |
307 m_userName = ""; | 314 m_userName = ""; |
308 | 315 |
316 m_proc = new QProcess; | |
317 | |
318 QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); | |
319 env.insert("LANG", "en_US.utf8"); | |
320 env.insert("LC_ALL", "en_US.utf8"); | |
321 env.insert("HGPLAIN", "1"); | |
322 m_proc->setProcessEnvironment(env); | |
323 | |
324 connect(m_proc, SIGNAL(started()), this, SLOT(started())); | |
325 connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)), | |
326 this, SLOT(finished(int, QProcess::ExitStatus))); | |
327 connect(m_proc, SIGNAL(readyReadStandardOutput()), | |
328 this, SLOT(dataReadyStdout())); | |
329 connect(m_proc, SIGNAL(readyReadStandardError()), | |
330 this, SLOT(dataReadyStderr())); | |
331 | |
309 if (!action.workingDir.isEmpty()) { | 332 if (!action.workingDir.isEmpty()) { |
310 m_proc->setWorkingDirectory(action.workingDir); | 333 m_proc->setWorkingDirectory(action.workingDir); |
311 } | 334 } |
312 | 335 |
313 if (interactive) { | 336 if (interactive) { |
314 openTerminal(); | 337 openTerminal(); |
315 if (m_ptySlaveFilename != "") { | 338 if (m_ptySlaveFilename != "") { |
339 DEBUG << "HgRunner: connecting to pseudoterminal" << endl; | |
316 m_proc->setStandardInputFile(m_ptySlaveFilename); | 340 m_proc->setStandardInputFile(m_ptySlaveFilename); |
341 m_proc->setStandardOutputFile(m_ptySlaveFilename); | |
342 // m_proc->setStandardErrorFile(m_ptySlaveFilename); | |
317 } | 343 } |
318 } | 344 } |
319 | 345 |
320 QString cmdline = executable; | 346 QString cmdline = executable; |
321 foreach (QString param, params) cmdline += " " + param; | 347 foreach (QString param, params) cmdline += " " + param; |
322 DEBUG << "HgRunner: starting: " << cmdline << " with cwd " | 348 DEBUG << "HgRunner: starting: " << cmdline << " with cwd " |
323 << action.workingDir << endl; | 349 << action.workingDir << endl; |
324 | 350 |
325 m_currentAction = action; | 351 m_currentAction = action; |
352 | |
353 // fill these out with what we actually ran | |
354 m_currentAction.executable = executable; | |
355 m_currentAction.params = params; | |
326 | 356 |
327 DEBUG << "set current action to " << m_currentAction.action << endl; | 357 DEBUG << "set current action to " << m_currentAction.action << endl; |
328 | 358 |
329 m_proc->start(executable, params); | 359 m_proc->start(executable, params); |
330 } | 360 } |
345 if (master < 0) { | 375 if (master < 0) { |
346 DEBUG << "openpt failed" << endl; | 376 DEBUG << "openpt failed" << endl; |
347 perror("openpt failed"); | 377 perror("openpt failed"); |
348 return; | 378 return; |
349 } | 379 } |
380 struct termios t; | |
381 if (tcgetattr(master, &t)) { | |
382 DEBUG << "tcgetattr failed" << endl; | |
383 perror("tcgetattr failed"); | |
384 } | |
385 cfmakeraw(&t); | |
386 if (tcsetattr(master, TCSANOW, &t)) { | |
387 DEBUG << "tcsetattr failed" << endl; | |
388 perror("tcsetattr failed"); | |
389 } | |
350 if (grantpt(master)) { | 390 if (grantpt(master)) { |
351 perror("grantpt failed"); | 391 perror("grantpt failed"); |
352 } | 392 } |
353 if (unlockpt(master)) { | 393 if (unlockpt(master)) { |
354 perror("unlockpt failed"); | 394 perror("unlockpt failed"); |
358 perror("ptsname failed"); | 398 perror("ptsname failed"); |
359 ::close(master); | 399 ::close(master); |
360 return; | 400 return; |
361 } | 401 } |
362 m_ptyMasterFd = master; | 402 m_ptyMasterFd = master; |
363 m_procInput = new QFile(); | 403 m_ptyFile = new QFile(); |
364 m_procInput->open(m_ptyMasterFd, QFile::WriteOnly); | 404 connect(m_ptyFile, SIGNAL(readyRead()), this, SLOT(dataReadyPty())); |
405 if (!m_ptyFile->open(m_ptyMasterFd, QFile::ReadWrite)) { | |
406 DEBUG << "HgRunner::openTerminal: Failed to open QFile on master fd" << endl; | |
407 } | |
365 m_ptySlaveFilename = slave; | 408 m_ptySlaveFilename = slave; |
366 DEBUG << "HgRunner::openTerminal: succeeded, slave is " | 409 DEBUG << "HgRunner::openTerminal: succeeded, slave is " |
367 << m_ptySlaveFilename << endl; | 410 << m_ptySlaveFilename << endl; |
368 #endif | 411 #endif |
369 } | 412 } |
370 | 413 |
371 void HgRunner::closeTerminal() | 414 void HgRunner::closeTerminal() |
372 { | 415 { |
373 #ifndef Q_OS_WIN32 | 416 #ifndef Q_OS_WIN32 |
374 if (m_ptySlaveFilename != "") { | 417 if (m_ptySlaveFilename != "") { |
375 delete m_procInput; | 418 delete m_ptyFile; |
376 m_procInput = 0; | 419 m_ptyFile = 0; |
377 ::close(m_ptyMasterFd); | 420 ::close(m_ptyMasterFd); |
378 m_ptySlaveFilename = ""; | 421 m_ptySlaveFilename = ""; |
379 } | 422 } |
380 #endif | 423 #endif |
381 } | 424 } |