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@84
|
50 m_proc->setProcessEnvironment(env);
|
Chris@84
|
51
|
Chris@84
|
52 m_proc->setProcessChannelMode(QProcess::MergedChannels);
|
cannam@45
|
53
|
jtkorhonen@0
|
54 setTextVisible(false);
|
jtkorhonen@0
|
55 setVisible(false);
|
Chris@84
|
56 m_isRunning = false;
|
jtkorhonen@0
|
57
|
Chris@84
|
58 m_output.clear();
|
jtkorhonen@0
|
59
|
Chris@84
|
60 connect(m_proc, SIGNAL(started()), this, SLOT(started()));
|
Chris@84
|
61 connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)),
|
Chris@62
|
62 this, SLOT(finished(int, QProcess::ExitStatus)));
|
Chris@84
|
63 connect(m_proc, SIGNAL(readyRead()), this, SLOT(dataReady()));
|
jtkorhonen@0
|
64 }
|
jtkorhonen@0
|
65
|
jtkorhonen@0
|
66 HgRunner::~HgRunner()
|
jtkorhonen@0
|
67 {
|
Chris@84
|
68 if (m_ptySlaveFilename != "") {
|
Chris@84
|
69 ::close(m_ptyMasterFd);
|
Chris@75
|
70 }
|
Chris@84
|
71 delete m_proc;
|
jtkorhonen@0
|
72 }
|
jtkorhonen@0
|
73
|
Chris@62
|
74 QString HgRunner::getHgBinaryName()
|
Chris@62
|
75 {
|
Chris@62
|
76 QSettings settings;
|
Chris@77
|
77 QString hg = settings.value("hgbinary", "").toString();
|
Chris@77
|
78 if (hg == "") {
|
Chris@77
|
79 hg = findExecutable("hg");
|
Chris@77
|
80 }
|
Chris@77
|
81 if (hg != "hg") {
|
Chris@77
|
82 settings.setValue("hgbinary", hg);
|
Chris@77
|
83 }
|
Chris@62
|
84 return hg;
|
Chris@62
|
85 }
|
Chris@62
|
86
|
jtkorhonen@0
|
87 void HgRunner::started()
|
jtkorhonen@0
|
88 {
|
Chris@75
|
89 /*
|
Chris@75
|
90 if (procInput) procInput->write("blah\n");
|
Chris@75
|
91 if (procInput) procInput->write("blah\n");
|
Chris@75
|
92 if (procInput) {
|
Chris@75
|
93 procInput->close();
|
Chris@75
|
94 // ::close(ptyMasterFd);
|
Chris@75
|
95 }
|
jtkorhonen@0
|
96 proc -> closeWriteChannel();
|
Chris@75
|
97 */
|
jtkorhonen@0
|
98 }
|
jtkorhonen@0
|
99
|
jtkorhonen@0
|
100 void HgRunner::setProcExitInfo(int procExitCode, QProcess::ExitStatus procExitStatus)
|
jtkorhonen@0
|
101 {
|
Chris@84
|
102 m_exitCode = procExitCode;
|
Chris@84
|
103 m_exitStatus = procExitStatus;
|
jtkorhonen@0
|
104 }
|
jtkorhonen@0
|
105
|
jtkorhonen@0
|
106 QString HgRunner::getLastCommandLine()
|
jtkorhonen@0
|
107 {
|
Chris@84
|
108 return QString("Command line: " + m_lastHgCommand + " " + m_lastParams);
|
jtkorhonen@0
|
109 }
|
jtkorhonen@0
|
110
|
Chris@84
|
111 void HgRunner::noteUsername(QString name)
|
Chris@75
|
112 {
|
Chris@84
|
113 m_userName = name;
|
Chris@75
|
114 }
|
Chris@75
|
115
|
Chris@84
|
116 void HgRunner::noteRealm(QString realm)
|
Chris@75
|
117 {
|
Chris@84
|
118 m_realm = realm;
|
Chris@84
|
119 }
|
Chris@84
|
120
|
Chris@84
|
121 void HgRunner::getUsername()
|
Chris@84
|
122 {
|
Chris@84
|
123 if (m_procInput) {
|
Chris@84
|
124 bool ok = false;
|
Chris@84
|
125 QString prompt = tr("User name:");
|
Chris@84
|
126 if (m_realm != "") {
|
Chris@84
|
127 prompt = tr("User name for \"%1\":").arg(m_realm);
|
Chris@84
|
128 }
|
Chris@84
|
129 QString pwd = QInputDialog::getText
|
Chris@84
|
130 (qobject_cast<QWidget *>(parent()),
|
Chris@84
|
131 tr("Enter user name"), prompt,
|
Chris@84
|
132 QLineEdit::Normal, QString(), &ok);
|
Chris@84
|
133 if (ok) {
|
Chris@84
|
134 m_procInput->write(QString("%1\n").arg(pwd).toUtf8());
|
Chris@84
|
135 m_procInput->flush();
|
Chris@84
|
136 return;
|
Chris@84
|
137 }
|
Chris@84
|
138 }
|
Chris@84
|
139 // user cancelled or something went wrong
|
Chris@84
|
140 killCurrentCommand();
|
Chris@84
|
141 }
|
Chris@84
|
142
|
Chris@84
|
143 void HgRunner::getPassword()
|
Chris@84
|
144 {
|
Chris@84
|
145 if (m_procInput) {
|
Chris@84
|
146 bool ok = false;
|
Chris@84
|
147 QString prompt = tr("Password:");
|
Chris@84
|
148 if (m_userName != "") {
|
Chris@84
|
149 if (m_realm != "") {
|
Chris@84
|
150 prompt = tr("Password for \"%1\" at \"%2\":")
|
Chris@84
|
151 .arg(m_userName).arg(m_realm);
|
Chris@75
|
152 } else {
|
Chris@84
|
153 prompt = tr("Password for user \"%1\":")
|
Chris@84
|
154 .arg(m_userName);
|
Chris@75
|
155 }
|
Chris@75
|
156 }
|
Chris@84
|
157 QString pwd = QInputDialog::getText
|
Chris@84
|
158 (qobject_cast<QWidget *>(parent()),
|
Chris@84
|
159 tr("Enter password"), prompt,
|
Chris@84
|
160 QLineEdit::Password, QString(), &ok);
|
Chris@84
|
161 if (ok) {
|
Chris@84
|
162 m_procInput->write(QString("%1\n").arg(pwd).toUtf8());
|
Chris@84
|
163 m_procInput->flush();
|
Chris@84
|
164 return;
|
Chris@84
|
165 }
|
Chris@75
|
166 }
|
Chris@84
|
167 // user cancelled or something went wrong
|
Chris@84
|
168 killCurrentCommand();
|
Chris@84
|
169 }
|
Chris@84
|
170
|
Chris@84
|
171 void HgRunner::checkPrompts(QString chunk)
|
Chris@84
|
172 {
|
Chris@84
|
173 DEBUG << "checkPrompts: " << chunk << endl;
|
Chris@84
|
174
|
Chris@84
|
175 QString text = chunk.trimmed();
|
Chris@84
|
176 QString lower = text.toLower();
|
Chris@84
|
177 if (lower.endsWith("password:")) {
|
Chris@84
|
178 getPassword();
|
Chris@84
|
179 return;
|
Chris@84
|
180 }
|
Chris@84
|
181 if (lower.endsWith("user:")) {
|
Chris@84
|
182 getUsername();
|
Chris@84
|
183 return;
|
Chris@84
|
184 }
|
Chris@84
|
185 QRegExp userRe("\\buser:\\s*([^\\s]+)");
|
Chris@84
|
186 if (userRe.indexIn(text) >= 0) {
|
Chris@84
|
187 noteUsername(userRe.cap(1));
|
Chris@84
|
188 }
|
Chris@84
|
189 QRegExp realmRe("\\brealmr:\\s*([^\\s]+)");
|
Chris@84
|
190 if (realmRe.indexIn(text) >= 0) {
|
Chris@84
|
191 noteRealm(realmRe.cap(1));
|
Chris@84
|
192 }
|
Chris@84
|
193 }
|
Chris@84
|
194
|
Chris@84
|
195 void HgRunner::dataReady()
|
Chris@84
|
196 {
|
Chris@84
|
197 DEBUG << "dataReady" << endl;
|
Chris@84
|
198 QString chunk = QString::fromUtf8(m_proc->readAll());
|
Chris@84
|
199 m_output += chunk;
|
Chris@84
|
200 checkPrompts(chunk);
|
Chris@75
|
201 }
|
Chris@75
|
202
|
jtkorhonen@0
|
203 void HgRunner::finished(int procExitCode, QProcess::ExitStatus procExitStatus)
|
jtkorhonen@0
|
204 {
|
jtkorhonen@0
|
205 setProcExitInfo(procExitCode, procExitStatus);
|
Chris@84
|
206 m_isRunning = false;
|
Chris@84
|
207
|
Chris@84
|
208 closeProcInput();
|
jtkorhonen@0
|
209
|
Chris@74
|
210 if (procExitCode == 0 && procExitStatus == QProcess::NormalExit) {
|
Chris@84
|
211 DEBUG << "HgRunner::finished: Command completed successfully" << endl;
|
Chris@62
|
212 emit commandCompleted();
|
Chris@62
|
213 } else {
|
Chris@84
|
214 DEBUG << "HgRunner::finished: Command failed" << endl;
|
Chris@62
|
215 emit commandFailed();
|
jtkorhonen@0
|
216 }
|
jtkorhonen@0
|
217 }
|
jtkorhonen@0
|
218
|
Chris@62
|
219 bool HgRunner::isCommandRunning()
|
jtkorhonen@0
|
220 {
|
Chris@84
|
221 return m_isRunning;
|
jtkorhonen@0
|
222 }
|
jtkorhonen@0
|
223
|
Chris@62
|
224 void HgRunner::killCurrentCommand()
|
jtkorhonen@0
|
225 {
|
Chris@62
|
226 if (isCommandRunning()) {
|
Chris@84
|
227 m_proc -> kill();
|
jtkorhonen@0
|
228 }
|
jtkorhonen@0
|
229 }
|
jtkorhonen@0
|
230
|
Chris@62
|
231 void HgRunner::startHgCommand(QString workingDir, QStringList params)
|
Chris@62
|
232 {
|
Chris@62
|
233 startCommand(getHgBinaryName(), workingDir, params);
|
Chris@62
|
234 }
|
jtkorhonen@0
|
235
|
Chris@62
|
236 void HgRunner::startCommand(QString command, QString workingDir, QStringList params)
|
jtkorhonen@0
|
237 {
|
Chris@84
|
238 m_isRunning = true;
|
jtkorhonen@0
|
239 setRange(0, 0);
|
jtkorhonen@0
|
240 setVisible(true);
|
Chris@84
|
241 m_output.clear();
|
Chris@84
|
242 m_exitCode = 0;
|
Chris@84
|
243 m_exitStatus = QProcess::NormalExit;
|
Chris@84
|
244 m_realm = "";
|
Chris@84
|
245 m_userName = "";
|
jtkorhonen@0
|
246
|
Chris@84
|
247 if (!workingDir.isEmpty()) {
|
Chris@84
|
248 m_proc->setWorkingDirectory(workingDir);
|
jtkorhonen@0
|
249 }
|
jtkorhonen@0
|
250
|
Chris@84
|
251 m_procInput = 0;
|
Chris@84
|
252 #ifndef Q_OS_WIN32
|
Chris@84
|
253 char name[1024];
|
Chris@84
|
254 if (openpty(&m_ptyMasterFd, &m_ptySlaveFd, name, NULL, NULL)) {
|
Chris@84
|
255 perror("openpty failed");
|
Chris@84
|
256 } else {
|
Chris@84
|
257 DEBUG << "openpty succeeded: master " << m_ptyMasterFd
|
Chris@84
|
258 << " slave " << m_ptySlaveFd << " filename " << name << endl;
|
Chris@84
|
259 m_procInput = new QFile;
|
Chris@84
|
260 m_procInput->open(m_ptyMasterFd, QFile::WriteOnly);
|
Chris@84
|
261 m_ptySlaveFilename = name;
|
Chris@84
|
262 m_proc->setStandardInputFile(m_ptySlaveFilename);
|
Chris@84
|
263 ::close(m_ptySlaveFd);
|
Chris@84
|
264 }
|
Chris@84
|
265 #endif
|
Chris@84
|
266
|
Chris@84
|
267 m_lastHgCommand = command;
|
Chris@84
|
268 m_lastParams = params.join(" ");
|
jtkorhonen@0
|
269
|
Chris@62
|
270 QString cmdline = command;
|
Chris@57
|
271 foreach (QString param, params) cmdline += " " + param;
|
Chris@64
|
272 DEBUG << "HgRunner: starting: " << cmdline << " with cwd "
|
Chris@64
|
273 << workingDir << endl;
|
Chris@43
|
274
|
Chris@84
|
275 m_proc->start(command, params);
|
Chris@84
|
276 }
|
Chris@84
|
277
|
Chris@84
|
278 void HgRunner::closeProcInput()
|
Chris@84
|
279 {
|
Chris@84
|
280 DEBUG << "closeProcInput" << endl;
|
Chris@84
|
281
|
Chris@84
|
282 m_proc->closeWriteChannel();
|
Chris@84
|
283 #ifndef Q_OS_WIN32
|
Chris@84
|
284 if (m_ptySlaveFilename != "") {
|
Chris@84
|
285 ::close(m_ptyMasterFd);
|
Chris@84
|
286 m_ptySlaveFilename = "";
|
Chris@84
|
287 }
|
Chris@84
|
288 #endif
|
jtkorhonen@0
|
289 }
|
jtkorhonen@0
|
290
|
jtkorhonen@0
|
291 int HgRunner::getExitCode()
|
jtkorhonen@0
|
292 {
|
Chris@84
|
293 return m_exitCode;
|
jtkorhonen@0
|
294 }
|
jtkorhonen@0
|
295
|
Chris@84
|
296 QString HgRunner::getOutput()
|
jtkorhonen@0
|
297 {
|
Chris@84
|
298 return m_output;
|
jtkorhonen@0
|
299 }
|
jtkorhonen@0
|
300
|
jtkorhonen@0
|
301 void HgRunner::hideProgBar()
|
jtkorhonen@0
|
302 {
|
jtkorhonen@0
|
303 setVisible(false);
|
jtkorhonen@0
|
304 }
|
jtkorhonen@0
|
305
|
jtkorhonen@0
|
306
|