Chris@57: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@57: Chris@57: /* Chris@57: EasyMercurial Chris@57: Chris@57: Based on HgExplorer by Jari Korhonen Chris@57: Copyright (c) 2010 Jari Korhonen Chris@57: Copyright (c) 2010 Chris Cannam Chris@57: Copyright (c) 2010 Queen Mary, University of London Chris@57: Chris@57: This program is free software; you can redistribute it and/or Chris@57: modify it under the terms of the GNU General Public License as Chris@57: published by the Free Software Foundation; either version 2 of the Chris@57: License, or (at your option) any later version. See the file Chris@57: COPYING included with this distribution for more information. Chris@57: */ jtkorhonen@0: jtkorhonen@0: #include "hgrunner.h" Chris@62: #include "common.h" Chris@57: #include "debug.h" Chris@50: Chris@50: #include Chris@50: #include Chris@50: #include Chris@50: #include Chris@50: #include Chris@62: #include Chris@75: #include jtkorhonen@0: Chris@43: #include jtkorhonen@0: #include Chris@75: #include Chris@75: #include jtkorhonen@0: Chris@76: #ifndef Q_OS_WIN32 Chris@80: #ifdef Q_OS_MAC Chris@80: #include Chris@80: #else Chris@76: #include Chris@76: #endif Chris@80: #endif Chris@76: jtkorhonen@0: HgRunner::HgRunner(QWidget * parent): QProgressBar(parent) jtkorhonen@0: { Chris@84: m_proc = new QProcess(this); jtkorhonen@0: cannam@45: QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); cannam@45: env.insert("LANG", "en_US.utf8"); cannam@45: env.insert("LC_ALL", "en_US.utf8"); Chris@84: m_proc->setProcessEnvironment(env); Chris@84: Chris@84: m_proc->setProcessChannelMode(QProcess::MergedChannels); cannam@45: jtkorhonen@0: setTextVisible(false); jtkorhonen@0: setVisible(false); Chris@84: m_isRunning = false; jtkorhonen@0: Chris@84: m_output.clear(); jtkorhonen@0: Chris@84: connect(m_proc, SIGNAL(started()), this, SLOT(started())); Chris@84: connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)), Chris@62: this, SLOT(finished(int, QProcess::ExitStatus))); Chris@84: connect(m_proc, SIGNAL(readyRead()), this, SLOT(dataReady())); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: HgRunner::~HgRunner() jtkorhonen@0: { Chris@84: if (m_ptySlaveFilename != "") { Chris@84: ::close(m_ptyMasterFd); Chris@75: } Chris@84: delete m_proc; jtkorhonen@0: } jtkorhonen@0: Chris@62: QString HgRunner::getHgBinaryName() Chris@62: { Chris@62: QSettings settings; Chris@77: QString hg = settings.value("hgbinary", "").toString(); Chris@77: if (hg == "") { Chris@77: hg = findExecutable("hg"); Chris@77: } Chris@77: if (hg != "hg") { Chris@77: settings.setValue("hgbinary", hg); Chris@77: } Chris@62: return hg; Chris@62: } Chris@62: jtkorhonen@0: void HgRunner::started() jtkorhonen@0: { Chris@75: /* Chris@75: if (procInput) procInput->write("blah\n"); Chris@75: if (procInput) procInput->write("blah\n"); Chris@75: if (procInput) { Chris@75: procInput->close(); Chris@75: // ::close(ptyMasterFd); Chris@75: } jtkorhonen@0: proc -> closeWriteChannel(); Chris@75: */ jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void HgRunner::setProcExitInfo(int procExitCode, QProcess::ExitStatus procExitStatus) jtkorhonen@0: { Chris@84: m_exitCode = procExitCode; Chris@84: m_exitStatus = procExitStatus; jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: QString HgRunner::getLastCommandLine() jtkorhonen@0: { Chris@84: return QString("Command line: " + m_lastHgCommand + " " + m_lastParams); jtkorhonen@0: } jtkorhonen@0: Chris@84: void HgRunner::noteUsername(QString name) Chris@75: { Chris@84: m_userName = name; Chris@75: } Chris@75: Chris@84: void HgRunner::noteRealm(QString realm) Chris@75: { Chris@84: m_realm = realm; Chris@84: } Chris@84: Chris@84: void HgRunner::getUsername() Chris@84: { Chris@84: if (m_procInput) { Chris@84: bool ok = false; Chris@84: QString prompt = tr("User name:"); Chris@84: if (m_realm != "") { Chris@84: prompt = tr("User name for \"%1\":").arg(m_realm); Chris@84: } Chris@84: QString pwd = QInputDialog::getText Chris@84: (qobject_cast(parent()), Chris@84: tr("Enter user name"), prompt, Chris@84: QLineEdit::Normal, QString(), &ok); Chris@84: if (ok) { Chris@84: m_procInput->write(QString("%1\n").arg(pwd).toUtf8()); Chris@84: m_procInput->flush(); Chris@84: return; Chris@84: } Chris@84: } Chris@84: // user cancelled or something went wrong Chris@84: killCurrentCommand(); Chris@84: } Chris@84: Chris@84: void HgRunner::getPassword() Chris@84: { Chris@84: if (m_procInput) { Chris@84: bool ok = false; Chris@84: QString prompt = tr("Password:"); Chris@84: if (m_userName != "") { Chris@84: if (m_realm != "") { Chris@84: prompt = tr("Password for \"%1\" at \"%2\":") Chris@84: .arg(m_userName).arg(m_realm); Chris@75: } else { Chris@84: prompt = tr("Password for user \"%1\":") Chris@84: .arg(m_userName); Chris@75: } Chris@75: } Chris@84: QString pwd = QInputDialog::getText Chris@84: (qobject_cast(parent()), Chris@84: tr("Enter password"), prompt, Chris@84: QLineEdit::Password, QString(), &ok); Chris@84: if (ok) { Chris@84: m_procInput->write(QString("%1\n").arg(pwd).toUtf8()); Chris@84: m_procInput->flush(); Chris@84: return; Chris@84: } Chris@75: } Chris@84: // user cancelled or something went wrong Chris@84: killCurrentCommand(); Chris@84: } Chris@84: Chris@84: void HgRunner::checkPrompts(QString chunk) Chris@84: { Chris@84: DEBUG << "checkPrompts: " << chunk << endl; Chris@84: Chris@84: QString text = chunk.trimmed(); Chris@84: QString lower = text.toLower(); Chris@84: if (lower.endsWith("password:")) { Chris@84: getPassword(); Chris@84: return; Chris@84: } Chris@84: if (lower.endsWith("user:")) { Chris@84: getUsername(); Chris@84: return; Chris@84: } Chris@84: QRegExp userRe("\\buser:\\s*([^\\s]+)"); Chris@84: if (userRe.indexIn(text) >= 0) { Chris@84: noteUsername(userRe.cap(1)); Chris@84: } Chris@84: QRegExp realmRe("\\brealmr:\\s*([^\\s]+)"); Chris@84: if (realmRe.indexIn(text) >= 0) { Chris@84: noteRealm(realmRe.cap(1)); Chris@84: } Chris@84: } Chris@84: Chris@84: void HgRunner::dataReady() Chris@84: { Chris@84: DEBUG << "dataReady" << endl; Chris@84: QString chunk = QString::fromUtf8(m_proc->readAll()); Chris@84: m_output += chunk; Chris@84: checkPrompts(chunk); Chris@75: } Chris@75: jtkorhonen@0: void HgRunner::finished(int procExitCode, QProcess::ExitStatus procExitStatus) jtkorhonen@0: { jtkorhonen@0: setProcExitInfo(procExitCode, procExitStatus); Chris@84: m_isRunning = false; Chris@84: Chris@84: closeProcInput(); jtkorhonen@0: Chris@74: if (procExitCode == 0 && procExitStatus == QProcess::NormalExit) { Chris@84: DEBUG << "HgRunner::finished: Command completed successfully" << endl; Chris@62: emit commandCompleted(); Chris@62: } else { Chris@84: DEBUG << "HgRunner::finished: Command failed" << endl; Chris@62: emit commandFailed(); jtkorhonen@0: } jtkorhonen@0: } jtkorhonen@0: Chris@62: bool HgRunner::isCommandRunning() jtkorhonen@0: { Chris@84: return m_isRunning; jtkorhonen@0: } jtkorhonen@0: Chris@62: void HgRunner::killCurrentCommand() jtkorhonen@0: { Chris@62: if (isCommandRunning()) { Chris@84: m_proc -> kill(); jtkorhonen@0: } jtkorhonen@0: } jtkorhonen@0: Chris@62: void HgRunner::startHgCommand(QString workingDir, QStringList params) Chris@62: { Chris@62: startCommand(getHgBinaryName(), workingDir, params); Chris@62: } jtkorhonen@0: Chris@62: void HgRunner::startCommand(QString command, QString workingDir, QStringList params) jtkorhonen@0: { Chris@84: m_isRunning = true; jtkorhonen@0: setRange(0, 0); jtkorhonen@0: setVisible(true); Chris@84: m_output.clear(); Chris@84: m_exitCode = 0; Chris@84: m_exitStatus = QProcess::NormalExit; Chris@84: m_realm = ""; Chris@84: m_userName = ""; jtkorhonen@0: Chris@84: if (!workingDir.isEmpty()) { Chris@84: m_proc->setWorkingDirectory(workingDir); jtkorhonen@0: } jtkorhonen@0: Chris@84: m_procInput = 0; Chris@84: #ifndef Q_OS_WIN32 Chris@84: char name[1024]; Chris@84: if (openpty(&m_ptyMasterFd, &m_ptySlaveFd, name, NULL, NULL)) { Chris@84: perror("openpty failed"); Chris@84: } else { Chris@84: DEBUG << "openpty succeeded: master " << m_ptyMasterFd Chris@84: << " slave " << m_ptySlaveFd << " filename " << name << endl; Chris@84: m_procInput = new QFile; Chris@84: m_procInput->open(m_ptyMasterFd, QFile::WriteOnly); Chris@84: m_ptySlaveFilename = name; Chris@84: m_proc->setStandardInputFile(m_ptySlaveFilename); Chris@84: ::close(m_ptySlaveFd); Chris@84: } Chris@84: #endif Chris@84: Chris@84: m_lastHgCommand = command; Chris@84: m_lastParams = params.join(" "); jtkorhonen@0: Chris@62: QString cmdline = command; Chris@57: foreach (QString param, params) cmdline += " " + param; Chris@64: DEBUG << "HgRunner: starting: " << cmdline << " with cwd " Chris@64: << workingDir << endl; Chris@43: Chris@84: m_proc->start(command, params); Chris@84: } Chris@84: Chris@84: void HgRunner::closeProcInput() Chris@84: { Chris@84: DEBUG << "closeProcInput" << endl; Chris@84: Chris@84: m_proc->closeWriteChannel(); Chris@84: #ifndef Q_OS_WIN32 Chris@84: if (m_ptySlaveFilename != "") { Chris@84: ::close(m_ptyMasterFd); Chris@84: m_ptySlaveFilename = ""; Chris@84: } Chris@84: #endif jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: int HgRunner::getExitCode() jtkorhonen@0: { Chris@84: return m_exitCode; jtkorhonen@0: } jtkorhonen@0: Chris@84: QString HgRunner::getOutput() jtkorhonen@0: { Chris@84: return m_output; jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void HgRunner::hideProgBar() jtkorhonen@0: { jtkorhonen@0: setVisible(false); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: