annotate src/hgrunner.cpp @ 406:c567fed39559

Add ability to configure the ssh client; default to TortoisePlink on Windows
author Chris Cannam <chris.cannam@eecs.qmul.ac.uk>
date Thu, 26 May 2011 15:48:11 +0100
parents 5cc0d897eb26
children b45e6cd9f1d4
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@406 163 QString HgRunner::getSshBinaryName()
chris@406 164 {
chris@406 165 QSettings settings;
chris@406 166 settings.beginGroup("Locations");
chris@406 167 return settings.value("sshbinary", "").toString();
chris@406 168 }
chris@406 169
Chris@239 170 QString HgRunner::getExtensionLocation()
Chris@239 171 {
Chris@239 172 QSettings settings;
Chris@239 173 settings.beginGroup("Locations");
Chris@239 174 QString extpath = settings.value("extensionpath", "").toString();
Chris@239 175 if (extpath != "" && QFile(extpath).exists()) return extpath;
Chris@239 176 return "";
Chris@239 177 }
Chris@239 178
jtkorhonen@0 179 void HgRunner::started()
jtkorhonen@0 180 {
Chris@104 181 DEBUG << "started" << endl;
Chris@75 182 /*
Chris@104 183 m_proc->write("blah\n");
Chris@104 184 m_proc->write("blah\n");
Chris@104 185 m_proc -> closeWriteChannel();
Chris@75 186 */
jtkorhonen@0 187 }
jtkorhonen@0 188
Chris@84 189 void HgRunner::noteUsername(QString name)
Chris@75 190 {
Chris@84 191 m_userName = name;
Chris@75 192 }
Chris@75 193
Chris@84 194 void HgRunner::noteRealm(QString realm)
Chris@75 195 {
Chris@84 196 m_realm = realm;
Chris@84 197 }
Chris@84 198
Chris@84 199 void HgRunner::getUsername()
Chris@84 200 {
Chris@113 201 if (m_ptyFile) {
Chris@84 202 bool ok = false;
Chris@84 203 QString prompt = tr("User name:");
Chris@84 204 if (m_realm != "") {
Chris@84 205 prompt = tr("User name for \"%1\":").arg(m_realm);
Chris@84 206 }
Chris@84 207 QString pwd = QInputDialog::getText
Chris@84 208 (qobject_cast<QWidget *>(parent()),
Chris@84 209 tr("Enter user name"), prompt,
Chris@84 210 QLineEdit::Normal, QString(), &ok);
Chris@84 211 if (ok) {
Chris@113 212 m_ptyFile->write(QString("%1\n").arg(pwd).toUtf8());
Chris@113 213 m_ptyFile->flush();
Chris@84 214 return;
Chris@111 215 } else {
Chris@111 216 DEBUG << "HgRunner::getUsername: user cancelled" << endl;
Chris@111 217 killCurrentCommand();
Chris@111 218 return;
Chris@84 219 }
Chris@84 220 }
Chris@84 221 // user cancelled or something went wrong
Chris@111 222 DEBUG << "HgRunner::getUsername: something went wrong" << endl;
Chris@84 223 killCurrentCommand();
Chris@84 224 }
Chris@84 225
Chris@84 226 void HgRunner::getPassword()
Chris@84 227 {
Chris@113 228 if (m_ptyFile) {
Chris@84 229 bool ok = false;
Chris@84 230 QString prompt = tr("Password:");
Chris@84 231 if (m_userName != "") {
Chris@84 232 if (m_realm != "") {
Chris@84 233 prompt = tr("Password for \"%1\" at \"%2\":")
Chris@84 234 .arg(m_userName).arg(m_realm);
Chris@75 235 } else {
Chris@84 236 prompt = tr("Password for user \"%1\":")
Chris@84 237 .arg(m_userName);
Chris@75 238 }
Chris@75 239 }
Chris@84 240 QString pwd = QInputDialog::getText
Chris@84 241 (qobject_cast<QWidget *>(parent()),
Chris@84 242 tr("Enter password"), prompt,
Chris@84 243 QLineEdit::Password, QString(), &ok);
Chris@84 244 if (ok) {
Chris@113 245 m_ptyFile->write(QString("%1\n").arg(pwd).toUtf8());
Chris@113 246 m_ptyFile->flush();
Chris@84 247 return;
Chris@111 248 } else {
Chris@111 249 DEBUG << "HgRunner::getPassword: user cancelled" << endl;
Chris@111 250 killCurrentCommand();
Chris@111 251 return;
Chris@84 252 }
Chris@75 253 }
Chris@84 254 // user cancelled or something went wrong
Chris@111 255 DEBUG << "HgRunner::getPassword: something went wrong" << endl;
Chris@84 256 killCurrentCommand();
Chris@84 257 }
Chris@84 258
Chris@113 259 bool HgRunner::checkPrompts(QString chunk)
Chris@84 260 {
Chris@93 261 //DEBUG << "checkPrompts: " << chunk << endl;
Chris@84 262
Chris@128 263 if (!m_currentAction.mayBeInteractive()) return false;
Chris@128 264
Chris@84 265 QString text = chunk.trimmed();
Chris@84 266 QString lower = text.toLower();
Chris@84 267 if (lower.endsWith("password:")) {
Chris@84 268 getPassword();
Chris@113 269 return true;
Chris@84 270 }
Chris@128 271 if (lower.endsWith("user:") || lower.endsWith("username:")) {
Chris@84 272 getUsername();
Chris@113 273 return true;
Chris@84 274 }
Chris@128 275 QRegExp userRe("\\buser(name)?:\\s*([^\\s]+)");
Chris@84 276 if (userRe.indexIn(text) >= 0) {
Chris@128 277 noteUsername(userRe.cap(2));
Chris@84 278 }
Chris@84 279 QRegExp realmRe("\\brealmr:\\s*([^\\s]+)");
Chris@84 280 if (realmRe.indexIn(text) >= 0) {
Chris@84 281 noteRealm(realmRe.cap(1));
Chris@84 282 }
Chris@113 283 return false;
Chris@84 284 }
Chris@84 285
Chris@110 286 void HgRunner::dataReadyStdout()
Chris@84 287 {
Chris@110 288 DEBUG << "dataReadyStdout" << endl;
chris@385 289 if (!m_proc) return;
Chris@110 290 QString chunk = QString::fromUtf8(m_proc->readAllStandardOutput());
Chris@113 291 if (!checkPrompts(chunk)) {
Chris@113 292 m_stdout += chunk;
Chris@113 293 }
Chris@110 294 }
Chris@110 295
Chris@110 296 void HgRunner::dataReadyStderr()
Chris@110 297 {
Chris@110 298 DEBUG << "dataReadyStderr" << endl;
chris@385 299 if (!m_proc) return;
Chris@110 300 QString chunk = QString::fromUtf8(m_proc->readAllStandardError());
Chris@113 301 DEBUG << chunk;
Chris@113 302 if (!checkPrompts(chunk)) {
Chris@113 303 m_stderr += chunk;
Chris@113 304 }
Chris@113 305 }
Chris@113 306
Chris@113 307 void HgRunner::dataReadyPty()
Chris@113 308 {
Chris@113 309 DEBUG << "dataReadyPty" << endl;
Chris@113 310 QString chunk = QString::fromUtf8(m_ptyFile->readAll());
Chris@113 311 DEBUG << "chunk of " << chunk.length() << " chars" << endl;
Chris@113 312 if (!checkPrompts(chunk)) {
Chris@113 313 m_stdout += chunk;
Chris@113 314 }
Chris@75 315 }
Chris@75 316
Chris@189 317 void HgRunner::error(QProcess::ProcessError)
Chris@189 318 {
Chris@189 319 finished(-1, QProcess::CrashExit);
Chris@189 320 }
Chris@189 321
jtkorhonen@0 322 void HgRunner::finished(int procExitCode, QProcess::ExitStatus procExitStatus)
jtkorhonen@0 323 {
chris@385 324 if (!m_proc) return;
chris@385 325
chris@385 326 // Save the current action and reset m_currentAction before we
Chris@109 327 // emit a signal to mark the completion; otherwise we may be
Chris@109 328 // resetting the action after a slot has already tried to set it
Chris@109 329 // to something else to start a new action
Chris@109 330
Chris@109 331 HgAction completedAction = m_currentAction;
Chris@109 332
Chris@84 333 m_isRunning = false;
Chris@109 334 m_currentAction = HgAction();
Chris@84 335
Chris@195 336 //closeProcInput();
Chris@189 337 m_proc->deleteLater();
Chris@113 338 m_proc = 0;
jtkorhonen@0 339
Chris@109 340 if (completedAction.action == ACT_NONE) {
Chris@109 341 DEBUG << "HgRunner::finished: WARNING: completed action is ACT_NONE" << endl;
Chris@62 342 } else {
Chris@113 343 if (procExitCode == 0 && procExitStatus == QProcess::NormalExit) {
Chris@113 344 DEBUG << "HgRunner::finished: Command completed successfully"
Chris@113 345 << endl;
Chris@124 346 // DEBUG << "stdout is " << m_stdout << endl;
Chris@113 347 emit commandCompleted(completedAction, m_stdout);
Chris@113 348 } else {
Chris@113 349 DEBUG << "HgRunner::finished: Command failed, exit code "
Chris@113 350 << procExitCode << ", exit status " << procExitStatus
Chris@113 351 << ", stderr follows" << endl;
Chris@113 352 DEBUG << m_stderr << endl;
Chris@113 353 emit commandFailed(completedAction, m_stderr);
Chris@113 354 }
jtkorhonen@0 355 }
Chris@109 356
Chris@109 357 checkQueue();
jtkorhonen@0 358 }
jtkorhonen@0 359
Chris@182 360 void HgRunner::killCurrentActions()
Chris@182 361 {
Chris@182 362 m_queue.clear();
Chris@182 363 killCurrentCommand();
Chris@182 364 }
Chris@182 365
Chris@62 366 void HgRunner::killCurrentCommand()
jtkorhonen@0 367 {
Chris@109 368 if (m_isRunning) {
Chris@113 369 m_currentAction.action = ACT_NONE; // so that we don't bother to notify
chris@385 370 if (m_proc) m_proc->kill();
jtkorhonen@0 371 }
jtkorhonen@0 372 }
jtkorhonen@0 373
Chris@109 374 void HgRunner::checkQueue()
Chris@62 375 {
Chris@109 376 if (m_isRunning) {
Chris@109 377 return;
Chris@109 378 }
Chris@109 379 if (m_queue.empty()) {
Chris@109 380 hide();
Chris@109 381 return;
Chris@109 382 }
Chris@109 383 HgAction toRun = m_queue.front();
Chris@109 384 m_queue.pop_front();
Chris@109 385 DEBUG << "checkQueue: have action: running " << toRun.action << endl;
Chris@109 386 startCommand(toRun);
Chris@109 387 }
Chris@109 388
Chris@109 389 void HgRunner::startCommand(HgAction action)
Chris@109 390 {
Chris@109 391 QString executable = action.executable;
Chris@109 392 bool interactive = false;
Chris@109 393 QStringList params = action.params;
Chris@109 394
Chris@340 395 if (action.workingDir.isEmpty()) {
Chris@340 396 // We require a working directory, never just operate in pwd
Chris@340 397 emit commandFailed(action, "EasyMercurial: No working directory supplied, will not run Mercurial command without one");
Chris@340 398 return;
Chris@340 399 }
Chris@340 400
Chris@257 401 QSettings settings;
Chris@257 402 settings.beginGroup("General");
Chris@257 403
Chris@109 404 if (executable == "") {
Chris@109 405 // This is a Hg command
Chris@239 406 executable = getHgBinaryName();
chris@406 407 if (executable == "") executable = "hg";
chris@406 408
chris@406 409 QString ssh = getSshBinaryName();
chris@406 410 if (ssh != "") {
chris@406 411 params.push_front(QString("ui.ssh=\"%1\"").arg(ssh));
chris@406 412 params.push_front("--config");
chris@406 413 }
Chris@161 414
Chris@161 415 if (action.mayBeInteractive()) {
Chris@161 416 params.push_front("ui.interactive=true");
Chris@161 417 params.push_front("--config");
Chris@176 418
Chris@176 419 if (settings.value("useextension", true).toBool()) {
Chris@239 420 QString extpath = getExtensionLocation();
chris@406 421 params.push_front(QString("extensions.easyhg=\"%1\"").arg(extpath));
Chris@176 422 params.push_front("--config");
Chris@176 423 }
Chris@161 424 interactive = true;
Chris@161 425 }
Chris@161 426
Chris@161 427 //!!! want an option to use the mercurial_keyring extension as well
Chris@107 428 }
jtkorhonen@0 429
Chris@84 430 m_isRunning = true;
jtkorhonen@0 431 setRange(0, 0);
Chris@115 432 if (!action.shouldBeFast()) show();
Chris@110 433 m_stdout.clear();
Chris@110 434 m_stderr.clear();
Chris@84 435 m_realm = "";
Chris@84 436 m_userName = "";
jtkorhonen@0 437
Chris@113 438 m_proc = new QProcess;
Chris@113 439
Chris@177 440 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
Chris@177 441
Chris@177 442 #ifdef Q_OS_WIN32
Chris@257 443 // On Win32 we like to bundle Hg and other executables with EasyHg
Chris@177 444 if (m_myDirPath != "") {
Chris@177 445 env.insert("PATH", m_myDirPath + ";" + env.value("PATH"));
Chris@172 446 }
Chris@172 447 #endif
Chris@172 448
Chris@257 449 #ifdef Q_OS_MAC
Chris@321 450 if (settings.value("python32", false).toBool()) {
Chris@261 451 env.insert("VERSIONER_PYTHON_PREFER_32_BIT", "1");
Chris@257 452 }
Chris@257 453 #endif
Chris@257 454
Chris@113 455 env.insert("LANG", "en_US.utf8");
Chris@113 456 env.insert("LC_ALL", "en_US.utf8");
Chris@113 457 env.insert("HGPLAIN", "1");
Chris@113 458 m_proc->setProcessEnvironment(env);
Chris@113 459
Chris@113 460 connect(m_proc, SIGNAL(started()), this, SLOT(started()));
Chris@189 461 connect(m_proc, SIGNAL(error(QProcess::ProcessError)),
Chris@189 462 this, SLOT(error(QProcess::ProcessError)));
Chris@113 463 connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)),
Chris@113 464 this, SLOT(finished(int, QProcess::ExitStatus)));
Chris@113 465 connect(m_proc, SIGNAL(readyReadStandardOutput()),
Chris@113 466 this, SLOT(dataReadyStdout()));
Chris@113 467 connect(m_proc, SIGNAL(readyReadStandardError()),
Chris@113 468 this, SLOT(dataReadyStderr()));
Chris@113 469
Chris@340 470 m_proc->setWorkingDirectory(action.workingDir);
jtkorhonen@0 471
Chris@107 472 if (interactive) {
Chris@111 473 openTerminal();
Chris@111 474 if (m_ptySlaveFilename != "") {
Chris@113 475 DEBUG << "HgRunner: connecting to pseudoterminal" << endl;
Chris@107 476 m_proc->setStandardInputFile(m_ptySlaveFilename);
Chris@114 477 // m_proc->setStandardOutputFile(m_ptySlaveFilename);
Chris@113 478 // m_proc->setStandardErrorFile(m_ptySlaveFilename);
Chris@107 479 }
Chris@84 480 }
Chris@84 481
Chris@109 482 QString cmdline = executable;
Chris@57 483 foreach (QString param, params) cmdline += " " + param;
Chris@64 484 DEBUG << "HgRunner: starting: " << cmdline << " with cwd "
Chris@109 485 << action.workingDir << endl;
Chris@43 486
Chris@109 487 m_currentAction = action;
Chris@109 488
Chris@113 489 // fill these out with what we actually ran
Chris@113 490 m_currentAction.executable = executable;
Chris@113 491 m_currentAction.params = params;
Chris@113 492
Chris@109 493 DEBUG << "set current action to " << m_currentAction.action << endl;
Chris@109 494
Chris@238 495 emit commandStarting(action);
Chris@238 496
Chris@109 497 m_proc->start(executable, params);
Chris@84 498 }
Chris@84 499
Chris@84 500 void HgRunner::closeProcInput()
Chris@84 501 {
Chris@84 502 DEBUG << "closeProcInput" << endl;
Chris@84 503
chris@385 504 if (m_proc) m_proc->closeWriteChannel();
Chris@111 505 }
Chris@111 506
Chris@111 507 void HgRunner::openTerminal()
Chris@111 508 {
Chris@111 509 #ifndef Q_OS_WIN32
Chris@111 510 if (m_ptySlaveFilename != "") return; // already open
Chris@111 511 DEBUG << "HgRunner::openTerminal: trying to open new pty" << endl;
Chris@111 512 int master = posix_openpt(O_RDWR | O_NOCTTY);
Chris@111 513 if (master < 0) {
Chris@111 514 DEBUG << "openpt failed" << endl;
Chris@111 515 perror("openpt failed");
Chris@111 516 return;
Chris@111 517 }
Chris@113 518 struct termios t;
Chris@113 519 if (tcgetattr(master, &t)) {
Chris@113 520 DEBUG << "tcgetattr failed" << endl;
Chris@113 521 perror("tcgetattr failed");
Chris@113 522 }
Chris@113 523 cfmakeraw(&t);
Chris@113 524 if (tcsetattr(master, TCSANOW, &t)) {
Chris@113 525 DEBUG << "tcsetattr failed" << endl;
Chris@113 526 perror("tcsetattr failed");
Chris@113 527 }
Chris@111 528 if (grantpt(master)) {
Chris@111 529 perror("grantpt failed");
Chris@111 530 }
Chris@111 531 if (unlockpt(master)) {
Chris@111 532 perror("unlockpt failed");
Chris@111 533 }
Chris@111 534 char *slave = ptsname(master);
Chris@111 535 if (!slave) {
Chris@111 536 perror("ptsname failed");
Chris@111 537 ::close(master);
Chris@111 538 return;
Chris@111 539 }
Chris@111 540 m_ptyMasterFd = master;
Chris@113 541 m_ptyFile = new QFile();
Chris@113 542 connect(m_ptyFile, SIGNAL(readyRead()), this, SLOT(dataReadyPty()));
Chris@113 543 if (!m_ptyFile->open(m_ptyMasterFd, QFile::ReadWrite)) {
Chris@113 544 DEBUG << "HgRunner::openTerminal: Failed to open QFile on master fd" << endl;
Chris@113 545 }
Chris@111 546 m_ptySlaveFilename = slave;
Chris@111 547 DEBUG << "HgRunner::openTerminal: succeeded, slave is "
Chris@111 548 << m_ptySlaveFilename << endl;
Chris@111 549 #endif
Chris@111 550 }
Chris@111 551
Chris@111 552 void HgRunner::closeTerminal()
Chris@111 553 {
Chris@84 554 #ifndef Q_OS_WIN32
Chris@84 555 if (m_ptySlaveFilename != "") {
Chris@113 556 delete m_ptyFile;
Chris@113 557 m_ptyFile = 0;
Chris@84 558 ::close(m_ptyMasterFd);
Chris@84 559 m_ptySlaveFilename = "";
Chris@84 560 }
Chris@84 561 #endif
jtkorhonen@0 562 }