Chris@57
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@57
|
2
|
Chris@57
|
3 /*
|
Chris@57
|
4 EasyMercurial
|
Chris@57
|
5
|
Chris@57
|
6 Based on HgExplorer by Jari Korhonen
|
Chris@57
|
7 Copyright (c) 2010 Jari Korhonen
|
Chris@57
|
8 Copyright (c) 2010 Chris Cannam
|
Chris@57
|
9 Copyright (c) 2010 Queen Mary, University of London
|
Chris@57
|
10
|
Chris@57
|
11 This program is free software; you can redistribute it and/or
|
Chris@57
|
12 modify it under the terms of the GNU General Public License as
|
Chris@57
|
13 published by the Free Software Foundation; either version 2 of the
|
Chris@57
|
14 License, or (at your option) any later version. See the file
|
Chris@57
|
15 COPYING included with this distribution for more information.
|
Chris@57
|
16 */
|
jtkorhonen@0
|
17
|
jtkorhonen@0
|
18 #include "hgrunner.h"
|
Chris@62
|
19 #include "common.h"
|
Chris@57
|
20 #include "debug.h"
|
Chris@50
|
21
|
Chris@50
|
22 #include <QPushButton>
|
Chris@50
|
23 #include <QListWidget>
|
Chris@50
|
24 #include <QDialog>
|
Chris@50
|
25 #include <QLabel>
|
Chris@50
|
26 #include <QVBoxLayout>
|
Chris@62
|
27 #include <QSettings>
|
Chris@75
|
28 #include <QInputDialog>
|
jtkorhonen@0
|
29
|
Chris@43
|
30 #include <iostream>
|
jtkorhonen@0
|
31 #include <unistd.h>
|
Chris@75
|
32 #include <errno.h>
|
Chris@75
|
33 #include <stdio.h>
|
jtkorhonen@0
|
34
|
Chris@76
|
35 #ifndef Q_OS_WIN32
|
Chris@80
|
36 #ifdef Q_OS_MAC
|
Chris@80
|
37 #include <util.h>
|
Chris@80
|
38 #else
|
Chris@76
|
39 #include <pty.h>
|
Chris@76
|
40 #endif
|
Chris@80
|
41 #endif
|
Chris@76
|
42
|
jtkorhonen@0
|
43 HgRunner::HgRunner(QWidget * parent): QProgressBar(parent)
|
jtkorhonen@0
|
44 {
|
Chris@84
|
45 m_proc = new QProcess(this);
|
jtkorhonen@0
|
46
|
cannam@45
|
47 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
cannam@45
|
48 env.insert("LANG", "en_US.utf8");
|
cannam@45
|
49 env.insert("LC_ALL", "en_US.utf8");
|
Chris@104
|
50 env.insert("HGPLAIN", "1");
|
Chris@84
|
51 m_proc->setProcessEnvironment(env);
|
Chris@84
|
52
|
Chris@84
|
53 m_proc->setProcessChannelMode(QProcess::MergedChannels);
|
cannam@45
|
54
|
jtkorhonen@0
|
55 setTextVisible(false);
|
jtkorhonen@0
|
56 setVisible(false);
|
Chris@84
|
57 m_isRunning = false;
|
jtkorhonen@0
|
58
|
Chris@84
|
59 m_output.clear();
|
jtkorhonen@0
|
60
|
Chris@84
|
61 connect(m_proc, SIGNAL(started()), this, SLOT(started()));
|
Chris@84
|
62 connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)),
|
Chris@62
|
63 this, SLOT(finished(int, QProcess::ExitStatus)));
|
Chris@84
|
64 connect(m_proc, SIGNAL(readyRead()), this, SLOT(dataReady()));
|
jtkorhonen@0
|
65 }
|
jtkorhonen@0
|
66
|
jtkorhonen@0
|
67 HgRunner::~HgRunner()
|
jtkorhonen@0
|
68 {
|
Chris@84
|
69 if (m_ptySlaveFilename != "") {
|
Chris@84
|
70 ::close(m_ptyMasterFd);
|
Chris@75
|
71 }
|
Chris@84
|
72 delete m_proc;
|
jtkorhonen@0
|
73 }
|
jtkorhonen@0
|
74
|
Chris@62
|
75 QString HgRunner::getHgBinaryName()
|
Chris@62
|
76 {
|
Chris@62
|
77 QSettings settings;
|
Chris@77
|
78 QString hg = settings.value("hgbinary", "").toString();
|
Chris@77
|
79 if (hg == "") {
|
Chris@77
|
80 hg = findExecutable("hg");
|
Chris@77
|
81 }
|
Chris@77
|
82 if (hg != "hg") {
|
Chris@77
|
83 settings.setValue("hgbinary", hg);
|
Chris@77
|
84 }
|
Chris@62
|
85 return hg;
|
Chris@62
|
86 }
|
Chris@62
|
87
|
jtkorhonen@0
|
88 void HgRunner::started()
|
jtkorhonen@0
|
89 {
|
Chris@104
|
90 DEBUG << "started" << endl;
|
Chris@75
|
91 /*
|
Chris@104
|
92 m_proc->write("blah\n");
|
Chris@104
|
93 m_proc->write("blah\n");
|
Chris@104
|
94 m_proc -> closeWriteChannel();
|
Chris@75
|
95 */
|
jtkorhonen@0
|
96 }
|
jtkorhonen@0
|
97
|
jtkorhonen@0
|
98 void HgRunner::setProcExitInfo(int procExitCode, QProcess::ExitStatus procExitStatus)
|
jtkorhonen@0
|
99 {
|
Chris@84
|
100 m_exitCode = procExitCode;
|
Chris@84
|
101 m_exitStatus = procExitStatus;
|
jtkorhonen@0
|
102 }
|
jtkorhonen@0
|
103
|
jtkorhonen@0
|
104 QString HgRunner::getLastCommandLine()
|
jtkorhonen@0
|
105 {
|
Chris@84
|
106 return QString("Command line: " + m_lastHgCommand + " " + m_lastParams);
|
jtkorhonen@0
|
107 }
|
jtkorhonen@0
|
108
|
Chris@84
|
109 void HgRunner::noteUsername(QString name)
|
Chris@75
|
110 {
|
Chris@84
|
111 m_userName = name;
|
Chris@75
|
112 }
|
Chris@75
|
113
|
Chris@84
|
114 void HgRunner::noteRealm(QString realm)
|
Chris@75
|
115 {
|
Chris@84
|
116 m_realm = realm;
|
Chris@84
|
117 }
|
Chris@84
|
118
|
Chris@84
|
119 void HgRunner::getUsername()
|
Chris@84
|
120 {
|
Chris@84
|
121 if (m_procInput) {
|
Chris@84
|
122 bool ok = false;
|
Chris@84
|
123 QString prompt = tr("User name:");
|
Chris@84
|
124 if (m_realm != "") {
|
Chris@84
|
125 prompt = tr("User name for \"%1\":").arg(m_realm);
|
Chris@84
|
126 }
|
Chris@84
|
127 QString pwd = QInputDialog::getText
|
Chris@84
|
128 (qobject_cast<QWidget *>(parent()),
|
Chris@84
|
129 tr("Enter user name"), prompt,
|
Chris@84
|
130 QLineEdit::Normal, QString(), &ok);
|
Chris@84
|
131 if (ok) {
|
Chris@84
|
132 m_procInput->write(QString("%1\n").arg(pwd).toUtf8());
|
Chris@84
|
133 m_procInput->flush();
|
Chris@84
|
134 return;
|
Chris@84
|
135 }
|
Chris@84
|
136 }
|
Chris@84
|
137 // user cancelled or something went wrong
|
Chris@84
|
138 killCurrentCommand();
|
Chris@84
|
139 }
|
Chris@84
|
140
|
Chris@84
|
141 void HgRunner::getPassword()
|
Chris@84
|
142 {
|
Chris@84
|
143 if (m_procInput) {
|
Chris@84
|
144 bool ok = false;
|
Chris@84
|
145 QString prompt = tr("Password:");
|
Chris@84
|
146 if (m_userName != "") {
|
Chris@84
|
147 if (m_realm != "") {
|
Chris@84
|
148 prompt = tr("Password for \"%1\" at \"%2\":")
|
Chris@84
|
149 .arg(m_userName).arg(m_realm);
|
Chris@75
|
150 } else {
|
Chris@84
|
151 prompt = tr("Password for user \"%1\":")
|
Chris@84
|
152 .arg(m_userName);
|
Chris@75
|
153 }
|
Chris@75
|
154 }
|
Chris@84
|
155 QString pwd = QInputDialog::getText
|
Chris@84
|
156 (qobject_cast<QWidget *>(parent()),
|
Chris@84
|
157 tr("Enter password"), prompt,
|
Chris@84
|
158 QLineEdit::Password, QString(), &ok);
|
Chris@84
|
159 if (ok) {
|
Chris@84
|
160 m_procInput->write(QString("%1\n").arg(pwd).toUtf8());
|
Chris@84
|
161 m_procInput->flush();
|
Chris@84
|
162 return;
|
Chris@84
|
163 }
|
Chris@75
|
164 }
|
Chris@84
|
165 // user cancelled or something went wrong
|
Chris@84
|
166 killCurrentCommand();
|
Chris@84
|
167 }
|
Chris@84
|
168
|
Chris@84
|
169 void HgRunner::checkPrompts(QString chunk)
|
Chris@84
|
170 {
|
Chris@93
|
171 //DEBUG << "checkPrompts: " << chunk << endl;
|
Chris@84
|
172
|
Chris@84
|
173 QString text = chunk.trimmed();
|
Chris@84
|
174 QString lower = text.toLower();
|
Chris@84
|
175 if (lower.endsWith("password:")) {
|
Chris@84
|
176 getPassword();
|
Chris@84
|
177 return;
|
Chris@84
|
178 }
|
Chris@84
|
179 if (lower.endsWith("user:")) {
|
Chris@84
|
180 getUsername();
|
Chris@84
|
181 return;
|
Chris@84
|
182 }
|
Chris@84
|
183 QRegExp userRe("\\buser:\\s*([^\\s]+)");
|
Chris@84
|
184 if (userRe.indexIn(text) >= 0) {
|
Chris@84
|
185 noteUsername(userRe.cap(1));
|
Chris@84
|
186 }
|
Chris@84
|
187 QRegExp realmRe("\\brealmr:\\s*([^\\s]+)");
|
Chris@84
|
188 if (realmRe.indexIn(text) >= 0) {
|
Chris@84
|
189 noteRealm(realmRe.cap(1));
|
Chris@84
|
190 }
|
Chris@84
|
191 }
|
Chris@84
|
192
|
Chris@84
|
193 void HgRunner::dataReady()
|
Chris@84
|
194 {
|
Chris@84
|
195 DEBUG << "dataReady" << endl;
|
Chris@84
|
196 QString chunk = QString::fromUtf8(m_proc->readAll());
|
Chris@84
|
197 m_output += chunk;
|
Chris@84
|
198 checkPrompts(chunk);
|
Chris@75
|
199 }
|
Chris@75
|
200
|
jtkorhonen@0
|
201 void HgRunner::finished(int procExitCode, QProcess::ExitStatus procExitStatus)
|
jtkorhonen@0
|
202 {
|
jtkorhonen@0
|
203 setProcExitInfo(procExitCode, procExitStatus);
|
Chris@84
|
204 m_isRunning = false;
|
Chris@84
|
205
|
Chris@84
|
206 closeProcInput();
|
jtkorhonen@0
|
207
|
Chris@74
|
208 if (procExitCode == 0 && procExitStatus == QProcess::NormalExit) {
|
Chris@84
|
209 DEBUG << "HgRunner::finished: Command completed successfully" << endl;
|
Chris@62
|
210 emit commandCompleted();
|
Chris@62
|
211 } else {
|
Chris@84
|
212 DEBUG << "HgRunner::finished: Command failed" << endl;
|
Chris@62
|
213 emit commandFailed();
|
jtkorhonen@0
|
214 }
|
jtkorhonen@0
|
215 }
|
jtkorhonen@0
|
216
|
Chris@62
|
217 bool HgRunner::isCommandRunning()
|
jtkorhonen@0
|
218 {
|
Chris@84
|
219 return m_isRunning;
|
jtkorhonen@0
|
220 }
|
jtkorhonen@0
|
221
|
Chris@62
|
222 void HgRunner::killCurrentCommand()
|
jtkorhonen@0
|
223 {
|
Chris@62
|
224 if (isCommandRunning()) {
|
Chris@84
|
225 m_proc -> kill();
|
jtkorhonen@0
|
226 }
|
jtkorhonen@0
|
227 }
|
jtkorhonen@0
|
228
|
Chris@107
|
229 void HgRunner::startHgCommand(QString workingDir, QStringList params, bool interactive)
|
Chris@62
|
230 {
|
Chris@104
|
231 #ifdef Q_OS_WIN32
|
Chris@104
|
232 // This at least means we won't block on the non-working password prompt
|
Chris@104
|
233 params.push_front("ui.interactive=false");
|
Chris@104
|
234 #else
|
Chris@104
|
235 // password prompt should work here
|
Chris@107
|
236 if (interactive) {
|
Chris@107
|
237 params.push_front("ui.interactive=true");
|
Chris@107
|
238 } else {
|
Chris@107
|
239 params.push_front("ui.interactive=false");
|
Chris@107
|
240 }
|
Chris@104
|
241 #endif
|
Chris@104
|
242 params.push_front("--config");
|
Chris@107
|
243 startCommand(getHgBinaryName(), workingDir, params, interactive);
|
Chris@62
|
244 }
|
jtkorhonen@0
|
245
|
Chris@107
|
246 void HgRunner::startCommand(QString command, QString workingDir, QStringList params,
|
Chris@107
|
247 bool interactive)
|
jtkorhonen@0
|
248 {
|
Chris@84
|
249 m_isRunning = true;
|
jtkorhonen@0
|
250 setRange(0, 0);
|
jtkorhonen@0
|
251 setVisible(true);
|
Chris@84
|
252 m_output.clear();
|
Chris@84
|
253 m_exitCode = 0;
|
Chris@84
|
254 m_exitStatus = QProcess::NormalExit;
|
Chris@84
|
255 m_realm = "";
|
Chris@84
|
256 m_userName = "";
|
jtkorhonen@0
|
257
|
Chris@84
|
258 if (!workingDir.isEmpty()) {
|
Chris@84
|
259 m_proc->setWorkingDirectory(workingDir);
|
jtkorhonen@0
|
260 }
|
jtkorhonen@0
|
261
|
Chris@84
|
262 m_procInput = 0;
|
Chris@107
|
263 m_ptySlaveFilename = "";
|
Chris@107
|
264
|
Chris@84
|
265 #ifndef Q_OS_WIN32
|
Chris@107
|
266 if (interactive) {
|
Chris@107
|
267 char name[1024];
|
Chris@107
|
268 if (openpty(&m_ptyMasterFd, &m_ptySlaveFd, name, NULL, NULL)) {
|
Chris@107
|
269 perror("openpty failed");
|
Chris@107
|
270 } else {
|
Chris@107
|
271 DEBUG << "openpty succeeded: master " << m_ptyMasterFd
|
Chris@107
|
272 << " slave " << m_ptySlaveFd << " filename " << name << endl;
|
Chris@107
|
273 m_procInput = new QFile;
|
Chris@107
|
274 m_procInput->open(m_ptyMasterFd, QFile::WriteOnly);
|
Chris@107
|
275 m_ptySlaveFilename = name;
|
Chris@107
|
276 m_proc->setStandardInputFile(m_ptySlaveFilename);
|
Chris@107
|
277 ::close(m_ptySlaveFd);
|
Chris@107
|
278 }
|
Chris@84
|
279 }
|
Chris@84
|
280 #endif
|
Chris@84
|
281
|
Chris@84
|
282 m_lastHgCommand = command;
|
Chris@84
|
283 m_lastParams = params.join(" ");
|
jtkorhonen@0
|
284
|
Chris@62
|
285 QString cmdline = command;
|
Chris@57
|
286 foreach (QString param, params) cmdline += " " + param;
|
Chris@64
|
287 DEBUG << "HgRunner: starting: " << cmdline << " with cwd "
|
Chris@64
|
288 << workingDir << endl;
|
Chris@43
|
289
|
Chris@84
|
290 m_proc->start(command, params);
|
Chris@84
|
291 }
|
Chris@84
|
292
|
Chris@84
|
293 void HgRunner::closeProcInput()
|
Chris@84
|
294 {
|
Chris@84
|
295 DEBUG << "closeProcInput" << endl;
|
Chris@84
|
296
|
Chris@84
|
297 m_proc->closeWriteChannel();
|
Chris@84
|
298 #ifndef Q_OS_WIN32
|
Chris@84
|
299 if (m_ptySlaveFilename != "") {
|
Chris@84
|
300 ::close(m_ptyMasterFd);
|
Chris@84
|
301 m_ptySlaveFilename = "";
|
Chris@84
|
302 }
|
Chris@84
|
303 #endif
|
jtkorhonen@0
|
304 }
|
jtkorhonen@0
|
305
|
jtkorhonen@0
|
306 int HgRunner::getExitCode()
|
jtkorhonen@0
|
307 {
|
Chris@84
|
308 return m_exitCode;
|
jtkorhonen@0
|
309 }
|
jtkorhonen@0
|
310
|
Chris@84
|
311 QString HgRunner::getOutput()
|
jtkorhonen@0
|
312 {
|
Chris@84
|
313 return m_output;
|
jtkorhonen@0
|
314 }
|
jtkorhonen@0
|
315
|
jtkorhonen@0
|
316 void HgRunner::hideProgBar()
|
jtkorhonen@0
|
317 {
|
jtkorhonen@0
|
318 setVisible(false);
|
jtkorhonen@0
|
319 }
|
jtkorhonen@0
|
320
|
jtkorhonen@0
|
321
|