annotate src/hgrunner.cpp @ 425:ad106f5fe75f

Add "Ignore Files" and "Edit Ignored List" to Work menu (latter is subsumed from Advanced menu formerly). Also subsume Serve via HTTP into File menu as Share Repository, and add a more helpful description of it. Remove Advanced menu
author Chris Cannam
date Thu, 23 Jun 2011 10:58:32 +0100
parents 005633eed862
children 66ec8b2ee946
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@408 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@408 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@407 421 // Looks like this one must be without quotes, even though the SSH
chris@407 422 // one above only works on Windows if it has quotes (at least where
chris@407 423 // there is a space in the path). Odd
chris@407 424 params.push_front(QString("extensions.easyhg=%1").arg(extpath));
Chris@176 425 params.push_front("--config");
Chris@176 426 }
Chris@161 427 interactive = true;
Chris@161 428 }
Chris@161 429
Chris@161 430 //!!! want an option to use the mercurial_keyring extension as well
Chris@107 431 }
jtkorhonen@0 432
Chris@84 433 m_isRunning = true;
jtkorhonen@0 434 setRange(0, 0);
Chris@115 435 if (!action.shouldBeFast()) show();
Chris@110 436 m_stdout.clear();
Chris@110 437 m_stderr.clear();
Chris@84 438 m_realm = "";
Chris@84 439 m_userName = "";
jtkorhonen@0 440
Chris@113 441 m_proc = new QProcess;
Chris@113 442
Chris@177 443 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
Chris@177 444
Chris@177 445 #ifdef Q_OS_WIN32
Chris@257 446 // On Win32 we like to bundle Hg and other executables with EasyHg
Chris@177 447 if (m_myDirPath != "") {
Chris@177 448 env.insert("PATH", m_myDirPath + ";" + env.value("PATH"));
Chris@172 449 }
Chris@172 450 #endif
Chris@172 451
Chris@257 452 #ifdef Q_OS_MAC
Chris@321 453 if (settings.value("python32", false).toBool()) {
Chris@261 454 env.insert("VERSIONER_PYTHON_PREFER_32_BIT", "1");
Chris@257 455 }
Chris@257 456 #endif
Chris@257 457
Chris@113 458 env.insert("LANG", "en_US.utf8");
Chris@113 459 env.insert("LC_ALL", "en_US.utf8");
Chris@408 460 env.insert("HGENCODING", "utf8");
Chris@113 461 env.insert("HGPLAIN", "1");
Chris@113 462 m_proc->setProcessEnvironment(env);
Chris@113 463
Chris@113 464 connect(m_proc, SIGNAL(started()), this, SLOT(started()));
Chris@189 465 connect(m_proc, SIGNAL(error(QProcess::ProcessError)),
Chris@189 466 this, SLOT(error(QProcess::ProcessError)));
Chris@113 467 connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)),
Chris@113 468 this, SLOT(finished(int, QProcess::ExitStatus)));
Chris@113 469 connect(m_proc, SIGNAL(readyReadStandardOutput()),
Chris@113 470 this, SLOT(dataReadyStdout()));
Chris@113 471 connect(m_proc, SIGNAL(readyReadStandardError()),
Chris@113 472 this, SLOT(dataReadyStderr()));
Chris@113 473
Chris@340 474 m_proc->setWorkingDirectory(action.workingDir);
jtkorhonen@0 475
Chris@107 476 if (interactive) {
Chris@111 477 openTerminal();
Chris@111 478 if (m_ptySlaveFilename != "") {
Chris@113 479 DEBUG << "HgRunner: connecting to pseudoterminal" << endl;
Chris@107 480 m_proc->setStandardInputFile(m_ptySlaveFilename);
Chris@114 481 // m_proc->setStandardOutputFile(m_ptySlaveFilename);
Chris@113 482 // m_proc->setStandardErrorFile(m_ptySlaveFilename);
Chris@107 483 }
Chris@84 484 }
Chris@84 485
Chris@109 486 QString cmdline = executable;
Chris@57 487 foreach (QString param, params) cmdline += " " + param;
Chris@64 488 DEBUG << "HgRunner: starting: " << cmdline << " with cwd "
Chris@109 489 << action.workingDir << endl;
Chris@43 490
Chris@109 491 m_currentAction = action;
Chris@109 492
Chris@113 493 // fill these out with what we actually ran
Chris@113 494 m_currentAction.executable = executable;
Chris@113 495 m_currentAction.params = params;
Chris@113 496
Chris@109 497 DEBUG << "set current action to " << m_currentAction.action << endl;
Chris@109 498
Chris@238 499 emit commandStarting(action);
Chris@238 500
Chris@109 501 m_proc->start(executable, params);
Chris@84 502 }
Chris@84 503
Chris@84 504 void HgRunner::closeProcInput()
Chris@84 505 {
Chris@84 506 DEBUG << "closeProcInput" << endl;
Chris@84 507
chris@385 508 if (m_proc) m_proc->closeWriteChannel();
Chris@111 509 }
Chris@111 510
Chris@111 511 void HgRunner::openTerminal()
Chris@111 512 {
Chris@111 513 #ifndef Q_OS_WIN32
Chris@111 514 if (m_ptySlaveFilename != "") return; // already open
Chris@111 515 DEBUG << "HgRunner::openTerminal: trying to open new pty" << endl;
Chris@111 516 int master = posix_openpt(O_RDWR | O_NOCTTY);
Chris@111 517 if (master < 0) {
Chris@111 518 DEBUG << "openpt failed" << endl;
Chris@111 519 perror("openpt failed");
Chris@111 520 return;
Chris@111 521 }
Chris@113 522 struct termios t;
Chris@113 523 if (tcgetattr(master, &t)) {
Chris@113 524 DEBUG << "tcgetattr failed" << endl;
Chris@113 525 perror("tcgetattr failed");
Chris@113 526 }
Chris@113 527 cfmakeraw(&t);
Chris@113 528 if (tcsetattr(master, TCSANOW, &t)) {
Chris@113 529 DEBUG << "tcsetattr failed" << endl;
Chris@113 530 perror("tcsetattr failed");
Chris@113 531 }
Chris@111 532 if (grantpt(master)) {
Chris@111 533 perror("grantpt failed");
Chris@111 534 }
Chris@111 535 if (unlockpt(master)) {
Chris@111 536 perror("unlockpt failed");
Chris@111 537 }
Chris@111 538 char *slave = ptsname(master);
Chris@111 539 if (!slave) {
Chris@111 540 perror("ptsname failed");
Chris@111 541 ::close(master);
Chris@111 542 return;
Chris@111 543 }
Chris@111 544 m_ptyMasterFd = master;
Chris@113 545 m_ptyFile = new QFile();
Chris@113 546 connect(m_ptyFile, SIGNAL(readyRead()), this, SLOT(dataReadyPty()));
Chris@113 547 if (!m_ptyFile->open(m_ptyMasterFd, QFile::ReadWrite)) {
Chris@113 548 DEBUG << "HgRunner::openTerminal: Failed to open QFile on master fd" << endl;
Chris@113 549 }
Chris@111 550 m_ptySlaveFilename = slave;
Chris@111 551 DEBUG << "HgRunner::openTerminal: succeeded, slave is "
Chris@111 552 << m_ptySlaveFilename << endl;
Chris@111 553 #endif
Chris@111 554 }
Chris@111 555
Chris@111 556 void HgRunner::closeTerminal()
Chris@111 557 {
Chris@84 558 #ifndef Q_OS_WIN32
Chris@84 559 if (m_ptySlaveFilename != "") {
Chris@113 560 delete m_ptyFile;
Chris@113 561 m_ptyFile = 0;
Chris@84 562 ::close(m_ptyMasterFd);
Chris@84 563 m_ptySlaveFilename = "";
Chris@84 564 }
Chris@84 565 #endif
jtkorhonen@0 566 }