annotate hgrunner.cpp @ 344:ccc55539e066

If the user cancels the first startup dialog (it has no cancel button, but they could use the WM close button), go no further
author Chris Cannam
date Wed, 16 Mar 2011 10:25:06 +0000
parents bb189827f6d1
children
rev   line source
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@244 8 Copyright (c) 2011 Chris Cannam
Chris@244 9 Copyright (c) 2011 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@240 21 #include "settingsdialog.h"
Chris@50 22
Chris@50 23 #include <QPushButton>
Chris@50 24 #include <QListWidget>
Chris@50 25 #include <QDialog>
Chris@50 26 #include <QLabel>
Chris@50 27 #include <QVBoxLayout>
Chris@62 28 #include <QSettings>
Chris@75 29 #include <QInputDialog>
Chris@188 30 #include <QTemporaryFile>
Chris@161 31 #include <QDir>
jtkorhonen@0 32
Chris@43 33 #include <iostream>
Chris@75 34 #include <errno.h>
Chris@75 35 #include <stdio.h>
Chris@111 36 #include <stdlib.h>
jtkorhonen@0 37
Chris@76 38 #ifndef Q_OS_WIN32
Chris@111 39 #include <unistd.h>
Chris@113 40 #include <termios.h>
Chris@111 41 #include <fcntl.h>
Chris@80 42 #endif
Chris@76 43
Chris@172 44 HgRunner::HgRunner(QString myDirPath, QWidget * parent) :
Chris@172 45 QProgressBar(parent),
Chris@172 46 m_myDirPath(myDirPath)
jtkorhonen@0 47 {
Chris@113 48 m_proc = 0;
Chris@84 49
Chris@239 50 // Always unbundle the extension: even if it already exists (in
Chris@239 51 // case we're upgrading) and even if we're not going to use it (so
Chris@239 52 // that it's available in case someone wants to use it later,
Chris@239 53 // e.g. to fix a malfunctioning setup). But the path we actually
Chris@239 54 // prefer is the one in the settings first, if it exists; then the
Chris@239 55 // unbundled one; then anything in the path if for some reason
Chris@239 56 // unbundling failed
Chris@239 57 unbundleExtension();
Chris@239 58
jtkorhonen@0 59 setTextVisible(false);
jtkorhonen@0 60 setVisible(false);
Chris@84 61 m_isRunning = false;
jtkorhonen@0 62 }
jtkorhonen@0 63
jtkorhonen@0 64 HgRunner::~HgRunner()
jtkorhonen@0 65 {
Chris@111 66 closeTerminal();
Chris@120 67 if (m_proc) {
Chris@120 68 m_proc->kill();
Chris@122 69 m_proc->deleteLater();
Chris@120 70 }
jtkorhonen@0 71 }
jtkorhonen@0 72
Chris@188 73 QString HgRunner::getUnbundledFileName()
Chris@188 74 {
Chris@240 75 return SettingsDialog::getUnbundledExtensionFileName();
Chris@188 76 }
Chris@188 77
Chris@180 78 QString HgRunner::unbundleExtension()
Chris@161 79 {
Chris@188 80 // Pull out the bundled Python file into a temporary file, and
Chris@188 81 // copy it to our known extension location, replacing the magic
Chris@188 82 // text NO_EASYHG_IMPORT_PATH with our installation location
Chris@188 83
Chris@161 84 QString bundled = ":easyhg.py";
Chris@188 85 QString unbundled = getUnbundledFileName();
Chris@188 86
Chris@188 87 QString target = QFileInfo(unbundled).path();
Chris@161 88 if (!QDir().mkpath(target)) {
Chris@161 89 DEBUG << "Failed to make unbundle path " << target << endl;
Chris@188 90 std::cerr << "Failed to make unbundle path " << target << std::endl;
Chris@180 91 return "";
Chris@161 92 }
Chris@188 93
Chris@161 94 QFile bf(bundled);
Chris@188 95 DEBUG << "unbundle: bundled file will be " << bundled << endl;
Chris@188 96 if (!bf.exists() || !bf.open(QIODevice::ReadOnly)) {
Chris@180 97 DEBUG << "Bundled extension is missing!" << endl;
Chris@180 98 return "";
Chris@180 99 }
Chris@188 100
Chris@188 101 QTemporaryFile tmpfile(QString("%1/easyhg.py.XXXXXX").arg(target));
Chris@188 102 tmpfile.setAutoRemove(false);
Chris@188 103 DEBUG << "unbundle: temp file will be " << tmpfile.fileName() << endl;
Chris@188 104 if (!tmpfile.open()) {
Chris@188 105 DEBUG << "Failed to open temporary file " << tmpfile.fileName() << endl;
Chris@188 106 std::cerr << "Failed to open temporary file " << tmpfile.fileName() << std::endl;
Chris@180 107 return "";
Chris@161 108 }
Chris@188 109
Chris@188 110 QString all = QString::fromUtf8(bf.readAll());
Chris@188 111 all.replace("NO_EASYHG_IMPORT_PATH", m_myDirPath);
Chris@188 112 tmpfile.write(all.toUtf8());
Chris@188 113 DEBUG << "unbundle: wrote " << all.length() << " characters" << endl;
Chris@188 114
Chris@188 115 tmpfile.close();
Chris@188 116
Chris@188 117 QFile ef(unbundled);
Chris@188 118 if (ef.exists()) {
Chris@188 119 DEBUG << "unbundle: removing old file " << unbundled << endl;
Chris@188 120 ef.remove();
Chris@188 121 }
Chris@188 122 DEBUG << "unbundle: renaming " << tmpfile.fileName() << " to " << unbundled << endl;
Chris@188 123 if (!tmpfile.rename(unbundled)) {
Chris@188 124 DEBUG << "Failed to move temporary file to target file " << unbundled << endl;
Chris@188 125 std::cerr << "Failed to move temporary file to target file " << unbundled << std::endl;
Chris@188 126 return "";
Chris@188 127 }
Chris@188 128
Chris@188 129 DEBUG << "Unbundled extension to " << unbundled << endl;
Chris@188 130 return unbundled;
Chris@161 131 }
Chris@161 132
Chris@109 133 void HgRunner::requestAction(HgAction action)
Chris@109 134 {
Chris@109 135 DEBUG << "requestAction " << action.action << endl;
Chris@109 136 bool pushIt = true;
Chris@109 137 if (m_queue.empty()) {
Chris@109 138 if (action == m_currentAction) {
Chris@109 139 // this request is identical to the thing we're executing
Chris@109 140 DEBUG << "requestAction: we're already handling this one, ignoring identical request" << endl;
Chris@109 141 pushIt = false;
Chris@109 142 }
Chris@109 143 } else {
Chris@109 144 HgAction last = m_queue.back();
Chris@109 145 if (action == last) {
Chris@109 146 // this request is identical to the previous thing we
Chris@109 147 // queued which we haven't executed yet
Chris@109 148 DEBUG << "requestAction: we're already queueing this one, ignoring identical request" << endl;
Chris@109 149 pushIt = false;
Chris@109 150 }
Chris@109 151 }
Chris@109 152 if (pushIt) m_queue.push_back(action);
Chris@109 153 checkQueue();
Chris@109 154 }
Chris@109 155
Chris@239 156 QString HgRunner::getHgBinaryName()
Chris@62 157 {
Chris@62 158 QSettings settings;
Chris@175 159 settings.beginGroup("Locations");
Chris@239 160 return settings.value("hgbinary", "").toString();
Chris@62 161 }
Chris@62 162
Chris@239 163 QString HgRunner::getExtensionLocation()
Chris@239 164 {
Chris@239 165 QSettings settings;
Chris@239 166 settings.beginGroup("Locations");
Chris@239 167 QString extpath = settings.value("extensionpath", "").toString();
Chris@239 168 if (extpath != "" && QFile(extpath).exists()) return extpath;
Chris@239 169 return "";
Chris@239 170 }
Chris@239 171
jtkorhonen@0 172 void HgRunner::started()
jtkorhonen@0 173 {
Chris@104 174 DEBUG << "started" << endl;
Chris@75 175 /*
Chris@104 176 m_proc->write("blah\n");
Chris@104 177 m_proc->write("blah\n");
Chris@104 178 m_proc -> closeWriteChannel();
Chris@75 179 */
jtkorhonen@0 180 }
jtkorhonen@0 181
Chris@84 182 void HgRunner::noteUsername(QString name)
Chris@75 183 {
Chris@84 184 m_userName = name;
Chris@75 185 }
Chris@75 186
Chris@84 187 void HgRunner::noteRealm(QString realm)
Chris@75 188 {
Chris@84 189 m_realm = realm;
Chris@84 190 }
Chris@84 191
Chris@84 192 void HgRunner::getUsername()
Chris@84 193 {
Chris@113 194 if (m_ptyFile) {
Chris@84 195 bool ok = false;
Chris@84 196 QString prompt = tr("User name:");
Chris@84 197 if (m_realm != "") {
Chris@84 198 prompt = tr("User name for \"%1\":").arg(m_realm);
Chris@84 199 }
Chris@84 200 QString pwd = QInputDialog::getText
Chris@84 201 (qobject_cast<QWidget *>(parent()),
Chris@84 202 tr("Enter user name"), prompt,
Chris@84 203 QLineEdit::Normal, QString(), &ok);
Chris@84 204 if (ok) {
Chris@113 205 m_ptyFile->write(QString("%1\n").arg(pwd).toUtf8());
Chris@113 206 m_ptyFile->flush();
Chris@84 207 return;
Chris@111 208 } else {
Chris@111 209 DEBUG << "HgRunner::getUsername: user cancelled" << endl;
Chris@111 210 killCurrentCommand();
Chris@111 211 return;
Chris@84 212 }
Chris@84 213 }
Chris@84 214 // user cancelled or something went wrong
Chris@111 215 DEBUG << "HgRunner::getUsername: something went wrong" << endl;
Chris@84 216 killCurrentCommand();
Chris@84 217 }
Chris@84 218
Chris@84 219 void HgRunner::getPassword()
Chris@84 220 {
Chris@113 221 if (m_ptyFile) {
Chris@84 222 bool ok = false;
Chris@84 223 QString prompt = tr("Password:");
Chris@84 224 if (m_userName != "") {
Chris@84 225 if (m_realm != "") {
Chris@84 226 prompt = tr("Password for \"%1\" at \"%2\":")
Chris@84 227 .arg(m_userName).arg(m_realm);
Chris@75 228 } else {
Chris@84 229 prompt = tr("Password for user \"%1\":")
Chris@84 230 .arg(m_userName);
Chris@75 231 }
Chris@75 232 }
Chris@84 233 QString pwd = QInputDialog::getText
Chris@84 234 (qobject_cast<QWidget *>(parent()),
Chris@84 235 tr("Enter password"), prompt,
Chris@84 236 QLineEdit::Password, QString(), &ok);
Chris@84 237 if (ok) {
Chris@113 238 m_ptyFile->write(QString("%1\n").arg(pwd).toUtf8());
Chris@113 239 m_ptyFile->flush();
Chris@84 240 return;
Chris@111 241 } else {
Chris@111 242 DEBUG << "HgRunner::getPassword: user cancelled" << endl;
Chris@111 243 killCurrentCommand();
Chris@111 244 return;
Chris@84 245 }
Chris@75 246 }
Chris@84 247 // user cancelled or something went wrong
Chris@111 248 DEBUG << "HgRunner::getPassword: something went wrong" << endl;
Chris@84 249 killCurrentCommand();
Chris@84 250 }
Chris@84 251
Chris@113 252 bool HgRunner::checkPrompts(QString chunk)
Chris@84 253 {
Chris@93 254 //DEBUG << "checkPrompts: " << chunk << endl;
Chris@84 255
Chris@128 256 if (!m_currentAction.mayBeInteractive()) return false;
Chris@128 257
Chris@84 258 QString text = chunk.trimmed();
Chris@84 259 QString lower = text.toLower();
Chris@84 260 if (lower.endsWith("password:")) {
Chris@84 261 getPassword();
Chris@113 262 return true;
Chris@84 263 }
Chris@128 264 if (lower.endsWith("user:") || lower.endsWith("username:")) {
Chris@84 265 getUsername();
Chris@113 266 return true;
Chris@84 267 }
Chris@128 268 QRegExp userRe("\\buser(name)?:\\s*([^\\s]+)");
Chris@84 269 if (userRe.indexIn(text) >= 0) {
Chris@128 270 noteUsername(userRe.cap(2));
Chris@84 271 }
Chris@84 272 QRegExp realmRe("\\brealmr:\\s*([^\\s]+)");
Chris@84 273 if (realmRe.indexIn(text) >= 0) {
Chris@84 274 noteRealm(realmRe.cap(1));
Chris@84 275 }
Chris@113 276 return false;
Chris@84 277 }
Chris@84 278
Chris@110 279 void HgRunner::dataReadyStdout()
Chris@84 280 {
Chris@110 281 DEBUG << "dataReadyStdout" << endl;
Chris@110 282 QString chunk = QString::fromUtf8(m_proc->readAllStandardOutput());
Chris@113 283 if (!checkPrompts(chunk)) {
Chris@113 284 m_stdout += chunk;
Chris@113 285 }
Chris@110 286 }
Chris@110 287
Chris@110 288 void HgRunner::dataReadyStderr()
Chris@110 289 {
Chris@110 290 DEBUG << "dataReadyStderr" << endl;
Chris@110 291 QString chunk = QString::fromUtf8(m_proc->readAllStandardError());
Chris@113 292 DEBUG << chunk;
Chris@113 293 if (!checkPrompts(chunk)) {
Chris@113 294 m_stderr += chunk;
Chris@113 295 }
Chris@113 296 }
Chris@113 297
Chris@113 298 void HgRunner::dataReadyPty()
Chris@113 299 {
Chris@113 300 DEBUG << "dataReadyPty" << endl;
Chris@113 301 QString chunk = QString::fromUtf8(m_ptyFile->readAll());
Chris@113 302 DEBUG << "chunk of " << chunk.length() << " chars" << endl;
Chris@113 303 if (!checkPrompts(chunk)) {
Chris@113 304 m_stdout += chunk;
Chris@113 305 }
Chris@75 306 }
Chris@75 307
Chris@189 308 void HgRunner::error(QProcess::ProcessError)
Chris@189 309 {
Chris@189 310 finished(-1, QProcess::CrashExit);
Chris@189 311 }
Chris@189 312
jtkorhonen@0 313 void HgRunner::finished(int procExitCode, QProcess::ExitStatus procExitStatus)
jtkorhonen@0 314 {
Chris@109 315 // Save the current action and reset m_currentAction before we
Chris@109 316 // emit a signal to mark the completion; otherwise we may be
Chris@109 317 // resetting the action after a slot has already tried to set it
Chris@109 318 // to something else to start a new action
Chris@109 319
Chris@109 320 HgAction completedAction = m_currentAction;
Chris@109 321
Chris@84 322 m_isRunning = false;
Chris@109 323 m_currentAction = HgAction();
Chris@84 324
Chris@195 325 //closeProcInput();
Chris@189 326 m_proc->deleteLater();
Chris@113 327 m_proc = 0;
jtkorhonen@0 328
Chris@109 329 if (completedAction.action == ACT_NONE) {
Chris@109 330 DEBUG << "HgRunner::finished: WARNING: completed action is ACT_NONE" << endl;
Chris@62 331 } else {
Chris@113 332 if (procExitCode == 0 && procExitStatus == QProcess::NormalExit) {
Chris@113 333 DEBUG << "HgRunner::finished: Command completed successfully"
Chris@113 334 << endl;
Chris@124 335 // DEBUG << "stdout is " << m_stdout << endl;
Chris@113 336 emit commandCompleted(completedAction, m_stdout);
Chris@113 337 } else {
Chris@113 338 DEBUG << "HgRunner::finished: Command failed, exit code "
Chris@113 339 << procExitCode << ", exit status " << procExitStatus
Chris@113 340 << ", stderr follows" << endl;
Chris@113 341 DEBUG << m_stderr << endl;
Chris@113 342 emit commandFailed(completedAction, m_stderr);
Chris@113 343 }
jtkorhonen@0 344 }
Chris@109 345
Chris@109 346 checkQueue();
jtkorhonen@0 347 }
jtkorhonen@0 348
Chris@182 349 void HgRunner::killCurrentActions()
Chris@182 350 {
Chris@182 351 m_queue.clear();
Chris@182 352 killCurrentCommand();
Chris@182 353 }
Chris@182 354
Chris@62 355 void HgRunner::killCurrentCommand()
jtkorhonen@0 356 {
Chris@109 357 if (m_isRunning) {
Chris@113 358 m_currentAction.action = ACT_NONE; // so that we don't bother to notify
Chris@113 359 m_proc->kill();
jtkorhonen@0 360 }
jtkorhonen@0 361 }
jtkorhonen@0 362
Chris@109 363 void HgRunner::checkQueue()
Chris@62 364 {
Chris@109 365 if (m_isRunning) {
Chris@109 366 return;
Chris@109 367 }
Chris@109 368 if (m_queue.empty()) {
Chris@109 369 hide();
Chris@109 370 return;
Chris@109 371 }
Chris@109 372 HgAction toRun = m_queue.front();
Chris@109 373 m_queue.pop_front();
Chris@109 374 DEBUG << "checkQueue: have action: running " << toRun.action << endl;
Chris@109 375 startCommand(toRun);
Chris@109 376 }
Chris@109 377
Chris@109 378 void HgRunner::startCommand(HgAction action)
Chris@109 379 {
Chris@109 380 QString executable = action.executable;
Chris@109 381 bool interactive = false;
Chris@109 382 QStringList params = action.params;
Chris@109 383
Chris@340 384 if (action.workingDir.isEmpty()) {
Chris@340 385 // We require a working directory, never just operate in pwd
Chris@340 386 emit commandFailed(action, "EasyMercurial: No working directory supplied, will not run Mercurial command without one");
Chris@340 387 return;
Chris@340 388 }
Chris@340 389
Chris@257 390 QSettings settings;
Chris@257 391 settings.beginGroup("General");
Chris@257 392
Chris@109 393 if (executable == "") {
Chris@109 394 // This is a Hg command
Chris@239 395 executable = getHgBinaryName();
Chris@161 396
Chris@161 397 if (action.mayBeInteractive()) {
Chris@161 398 params.push_front("ui.interactive=true");
Chris@161 399 params.push_front("--config");
Chris@176 400
Chris@176 401 if (settings.value("useextension", true).toBool()) {
Chris@239 402 QString extpath = getExtensionLocation();
Chris@180 403 params.push_front(QString("extensions.easyhg=%1").arg(extpath));
Chris@176 404 params.push_front("--config");
Chris@176 405 }
Chris@161 406 interactive = true;
Chris@161 407 }
Chris@161 408
Chris@161 409 //!!! want an option to use the mercurial_keyring extension as well
Chris@107 410 }
jtkorhonen@0 411
Chris@84 412 m_isRunning = true;
jtkorhonen@0 413 setRange(0, 0);
Chris@115 414 if (!action.shouldBeFast()) show();
Chris@110 415 m_stdout.clear();
Chris@110 416 m_stderr.clear();
Chris@84 417 m_realm = "";
Chris@84 418 m_userName = "";
jtkorhonen@0 419
Chris@113 420 m_proc = new QProcess;
Chris@113 421
Chris@177 422 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
Chris@177 423
Chris@177 424 #ifdef Q_OS_WIN32
Chris@257 425 // On Win32 we like to bundle Hg and other executables with EasyHg
Chris@177 426 if (m_myDirPath != "") {
Chris@177 427 env.insert("PATH", m_myDirPath + ";" + env.value("PATH"));
Chris@172 428 }
Chris@172 429 #endif
Chris@172 430
Chris@257 431 #ifdef Q_OS_MAC
Chris@321 432 if (settings.value("python32", false).toBool()) {
Chris@261 433 env.insert("VERSIONER_PYTHON_PREFER_32_BIT", "1");
Chris@257 434 }
Chris@257 435 #endif
Chris@257 436
Chris@113 437 env.insert("LANG", "en_US.utf8");
Chris@113 438 env.insert("LC_ALL", "en_US.utf8");
Chris@113 439 env.insert("HGPLAIN", "1");
Chris@113 440 m_proc->setProcessEnvironment(env);
Chris@113 441
Chris@113 442 connect(m_proc, SIGNAL(started()), this, SLOT(started()));
Chris@189 443 connect(m_proc, SIGNAL(error(QProcess::ProcessError)),
Chris@189 444 this, SLOT(error(QProcess::ProcessError)));
Chris@113 445 connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)),
Chris@113 446 this, SLOT(finished(int, QProcess::ExitStatus)));
Chris@113 447 connect(m_proc, SIGNAL(readyReadStandardOutput()),
Chris@113 448 this, SLOT(dataReadyStdout()));
Chris@113 449 connect(m_proc, SIGNAL(readyReadStandardError()),
Chris@113 450 this, SLOT(dataReadyStderr()));
Chris@113 451
Chris@340 452 m_proc->setWorkingDirectory(action.workingDir);
jtkorhonen@0 453
Chris@107 454 if (interactive) {
Chris@111 455 openTerminal();
Chris@111 456 if (m_ptySlaveFilename != "") {
Chris@113 457 DEBUG << "HgRunner: connecting to pseudoterminal" << endl;
Chris@107 458 m_proc->setStandardInputFile(m_ptySlaveFilename);
Chris@114 459 // m_proc->setStandardOutputFile(m_ptySlaveFilename);
Chris@113 460 // m_proc->setStandardErrorFile(m_ptySlaveFilename);
Chris@107 461 }
Chris@84 462 }
Chris@84 463
Chris@109 464 QString cmdline = executable;
Chris@57 465 foreach (QString param, params) cmdline += " " + param;
Chris@64 466 DEBUG << "HgRunner: starting: " << cmdline << " with cwd "
Chris@109 467 << action.workingDir << endl;
Chris@43 468
Chris@109 469 m_currentAction = action;
Chris@109 470
Chris@113 471 // fill these out with what we actually ran
Chris@113 472 m_currentAction.executable = executable;
Chris@113 473 m_currentAction.params = params;
Chris@113 474
Chris@109 475 DEBUG << "set current action to " << m_currentAction.action << endl;
Chris@109 476
Chris@238 477 emit commandStarting(action);
Chris@238 478
Chris@109 479 m_proc->start(executable, params);
Chris@84 480 }
Chris@84 481
Chris@84 482 void HgRunner::closeProcInput()
Chris@84 483 {
Chris@84 484 DEBUG << "closeProcInput" << endl;
Chris@84 485
Chris@84 486 m_proc->closeWriteChannel();
Chris@111 487 }
Chris@111 488
Chris@111 489 void HgRunner::openTerminal()
Chris@111 490 {
Chris@111 491 #ifndef Q_OS_WIN32
Chris@111 492 if (m_ptySlaveFilename != "") return; // already open
Chris@111 493 DEBUG << "HgRunner::openTerminal: trying to open new pty" << endl;
Chris@111 494 int master = posix_openpt(O_RDWR | O_NOCTTY);
Chris@111 495 if (master < 0) {
Chris@111 496 DEBUG << "openpt failed" << endl;
Chris@111 497 perror("openpt failed");
Chris@111 498 return;
Chris@111 499 }
Chris@113 500 struct termios t;
Chris@113 501 if (tcgetattr(master, &t)) {
Chris@113 502 DEBUG << "tcgetattr failed" << endl;
Chris@113 503 perror("tcgetattr failed");
Chris@113 504 }
Chris@113 505 cfmakeraw(&t);
Chris@113 506 if (tcsetattr(master, TCSANOW, &t)) {
Chris@113 507 DEBUG << "tcsetattr failed" << endl;
Chris@113 508 perror("tcsetattr failed");
Chris@113 509 }
Chris@111 510 if (grantpt(master)) {
Chris@111 511 perror("grantpt failed");
Chris@111 512 }
Chris@111 513 if (unlockpt(master)) {
Chris@111 514 perror("unlockpt failed");
Chris@111 515 }
Chris@111 516 char *slave = ptsname(master);
Chris@111 517 if (!slave) {
Chris@111 518 perror("ptsname failed");
Chris@111 519 ::close(master);
Chris@111 520 return;
Chris@111 521 }
Chris@111 522 m_ptyMasterFd = master;
Chris@113 523 m_ptyFile = new QFile();
Chris@113 524 connect(m_ptyFile, SIGNAL(readyRead()), this, SLOT(dataReadyPty()));
Chris@113 525 if (!m_ptyFile->open(m_ptyMasterFd, QFile::ReadWrite)) {
Chris@113 526 DEBUG << "HgRunner::openTerminal: Failed to open QFile on master fd" << endl;
Chris@113 527 }
Chris@111 528 m_ptySlaveFilename = slave;
Chris@111 529 DEBUG << "HgRunner::openTerminal: succeeded, slave is "
Chris@111 530 << m_ptySlaveFilename << endl;
Chris@111 531 #endif
Chris@111 532 }
Chris@111 533
Chris@111 534 void HgRunner::closeTerminal()
Chris@111 535 {
Chris@84 536 #ifndef Q_OS_WIN32
Chris@84 537 if (m_ptySlaveFilename != "") {
Chris@113 538 delete m_ptyFile;
Chris@113 539 m_ptyFile = 0;
Chris@84 540 ::close(m_ptyMasterFd);
Chris@84 541 m_ptySlaveFilename = "";
Chris@84 542 }
Chris@84 543 #endif
jtkorhonen@0 544 }