annotate src/common.cpp @ 571:012ba1b83328

Show cancel button with progress bar only when running an operation that it makes sense to cancel (we don't really want people cancelling e.g. initial folder scan because it would leave things in an inconsistent state)
author Chris Cannam
date Thu, 01 Mar 2012 22:53:54 +0000
parents 533519ebc0cb
children ae67ea0af696
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@560 8 Copyright (c) 2012 Chris Cannam
Chris@560 9 Copyright (c) 2012 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
Chris@62 18 #include "common.h"
Chris@62 19 #include "debug.h"
jtkorhonen@0 20
Chris@62 21 #include <QFileInfo>
Chris@62 22 #include <QProcessEnvironment>
Chris@62 23 #include <QStringList>
Chris@77 24 #include <QDir>
Chris@172 25 #include <QRegExp>
jtkorhonen@0 26
Chris@62 27 #include <sys/types.h>
Chris@76 28
Chris@76 29 #ifdef Q_OS_WIN32
Chris@76 30 #define _WIN32_WINNT 0x0500
Chris@166 31 #define SECURITY_WIN32
Chris@76 32 #include <windows.h>
Chris@76 33 #include <security.h>
Chris@455 34 #include <process.h>
Chris@76 35 #else
Chris@78 36 #include <errno.h>
Chris@62 37 #include <pwd.h>
Chris@78 38 #include <unistd.h>
Chris@80 39 #include <sys/ioctl.h>
Chris@80 40 #include <sys/types.h>
Chris@80 41 #include <sys/stat.h>
Chris@80 42 #include <fcntl.h>
Chris@105 43 #include <signal.h>
Chris@443 44 #include <stdio.h>
Chris@76 45 #endif
Chris@62 46
Chris@172 47 QString findInPath(QString name, QString installPath, bool executableRequired)
Chris@62 48 {
Chris@77 49 bool found = false;
Chris@62 50 if (name != "") {
Chris@172 51 if (name[0] != '/'
Chris@172 52 #ifdef Q_OS_WIN32
Chris@178 53 && (QRegExp("^\\w:").indexIn(name) != 0)
Chris@172 54 #endif
Chris@178 55 ) {
Chris@77 56 #ifdef Q_OS_WIN32
Chris@77 57 QChar pathSep = ';';
Chris@77 58 #else
Chris@77 59 QChar pathSep = ':';
Chris@77 60 #endif
Chris@62 61 name = QFileInfo(name).fileName();
Chris@62 62 QString path =
Chris@62 63 QProcessEnvironment::systemEnvironment().value("PATH");
Chris@214 64 DEBUG << "findInPath: seeking location for binary " << name
Chris@74 65 << ": system path is " << path << endl;
Chris@172 66 if (installPath != "") {
Chris@214 67 DEBUG << "findInPath: install path is " << installPath
Chris@172 68 << ", adding to system path" << endl;
Chris@172 69 //!!! path = path + pathSep + installPath;
Chris@172 70 path = installPath + pathSep + path;
Chris@172 71 }
Chris@74 72 #ifndef Q_OS_WIN32
Chris@178 73 //!!!
Chris@74 74 path = path + ":/usr/local/bin";
Chris@74 75 DEBUG << "... adding /usr/local/bin just in case (fix and add settings dlg please)"
Chris@74 76 << endl;
Chris@74 77 #endif
Chris@62 78 QStringList elements = path.split(pathSep, QString::SkipEmptyParts);
Chris@62 79 foreach (QString element, elements) {
Chris@77 80 QString full = QDir(element).filePath(name);
Chris@62 81 QFileInfo fi(full);
Chris@214 82 DEBUG << "findInPath: looking at " << full << endl;
Chris@172 83 if (fi.exists() && fi.isFile()) {
Chris@214 84 DEBUG << "findInPath: it's a file" << endl;
Chris@172 85 if (!executableRequired || fi.isExecutable()) {
Chris@172 86 name = full;
Chris@214 87 DEBUG << "findInPath: found at " << name << endl;
Chris@172 88 found = true;
Chris@172 89 break;
Chris@172 90 }
Chris@62 91 }
Chris@62 92 }
Chris@214 93 } else {
Chris@214 94 // absolute path given
Chris@214 95 QFileInfo fi(name);
Chris@214 96 DEBUG << "findInPath: looking at absolute path " << name << endl;
Chris@214 97 if (fi.exists() && fi.isFile()) {
Chris@214 98 DEBUG << "findInPath: it's a file" << endl;
Chris@214 99 if (!executableRequired || fi.isExecutable()) {
Chris@214 100 DEBUG << "findInPath: found at " << name << endl;
Chris@214 101 found = true;
Chris@214 102 }
Chris@214 103 }
Chris@62 104 }
Chris@62 105 }
Chris@77 106 #ifdef Q_OS_WIN32
Chris@77 107 if (!found) {
Chris@77 108 if (!name.endsWith(".exe")) {
Chris@172 109 return findInPath(name + ".exe", installPath, executableRequired);
Chris@77 110 }
Chris@77 111 }
Chris@77 112 #endif
Chris@213 113 if (found) {
Chris@213 114 return name;
Chris@213 115 } else {
Chris@213 116 return "";
Chris@213 117 }
Chris@62 118 }
jtkorhonen@0 119
Chris@62 120 #ifdef Q_OS_WIN32
Chris@62 121 QString getUserRealName()
Chris@62 122 {
Chris@76 123 TCHAR buf[1024];
Chris@76 124 long unsigned int maxlen = 1000;
Chris@62 125 LPTSTR info = buf;
jtkorhonen@0 126
Chris@76 127 if (!GetUserNameEx(NameDisplay, info, &maxlen)) {
Chris@76 128 DEBUG << "GetUserNameEx failed: " << GetLastError() << endl;
Chris@62 129 return "";
Chris@62 130 }
jtkorhonen@0 131
Chris@76 132 #ifdef UNICODE
Chris@76 133 return QString::fromUtf16((const unsigned short *)info);
Chris@76 134 #else
Chris@76 135 return QString::fromLocal8Bit(info);
Chris@76 136 #endif
Chris@62 137 }
luisf@71 138 #else
luisf@71 139 #ifdef Q_OS_MAC
Chris@62 140 // Nothing here: definition is in common_osx.mm
Chris@62 141 #else
Chris@62 142 QString getUserRealName()
Chris@62 143 {
Chris@62 144 const int maxlen = 1023;
Chris@62 145 char buf[maxlen + 2];
jtkorhonen@0 146
Chris@62 147 if (getlogin_r(buf, maxlen)) return "";
jtkorhonen@0 148
Chris@62 149 struct passwd *p = getpwnam(buf);
Chris@62 150 if (!p) return "";
Chris@62 151
Chris@62 152 QString s(p->pw_gecos);
Chris@62 153 if (s != "") s = s.split(',')[0];
Chris@62 154 return s;
Chris@62 155 }
Chris@62 156 #endif
luisf@71 157 #endif
jtkorhonen@0 158
Chris@78 159 void loseControllingTerminal()
Chris@78 160 {
Chris@78 161 #ifndef Q_OS_WIN32
Chris@80 162
Chris@80 163 if (!isatty(0)) {
Chris@80 164 DEBUG << "stdin is not a terminal" << endl;
Chris@80 165 } else {
Chris@80 166 DEBUG << "stdin is a terminal, detaching from it" << endl;
Chris@80 167 if (ioctl(0, TIOCNOTTY, NULL) < 0) {
Chris@80 168 perror("ioctl failed");
Chris@83 169 DEBUG << "ioctl for TIOCNOTTY on stdin failed (errno = " << errno << ")" << endl;
Chris@78 170 } else {
Chris@83 171 DEBUG << "ioctl for TIOCNOTTY on stdin succeeded" << endl;
Chris@83 172 return;
Chris@78 173 }
Chris@80 174 }
Chris@80 175
Chris@80 176 int ttyfd = open("/dev/tty", O_RDWR);
Chris@80 177 if (ttyfd < 0) {
Chris@80 178 DEBUG << "failed to open controlling terminal" << endl;
Chris@80 179 } else {
Chris@80 180 if (ioctl(ttyfd, TIOCNOTTY, NULL) < 0) {
Chris@80 181 perror("ioctl failed");
Chris@83 182 DEBUG << "ioctl for TIOCNOTTY on controlling terminal failed (errno = " << errno << ")" << endl;
Chris@80 183 } else {
Chris@83 184 DEBUG << "ioctl for TIOCNOTTY on controlling terminal succeeded" << endl;
Chris@83 185 return;
Chris@78 186 }
Chris@78 187 }
Chris@80 188
Chris@78 189 #endif
Chris@78 190 }
Chris@79 191
Chris@105 192 void installSignalHandlers()
Chris@105 193 {
Chris@105 194 #ifndef Q_OS_WIN32
Chris@105 195 sigset_t sgnals;
Chris@105 196 sigemptyset (&sgnals);
Chris@105 197 sigaddset(&sgnals, SIGHUP);
Chris@105 198 sigaddset(&sgnals, SIGCONT);
Chris@105 199 pthread_sigmask(SIG_BLOCK, &sgnals, 0);
Chris@105 200 #endif
Chris@105 201 }
Chris@105 202
Chris@455 203 ProcessStatus
Chris@455 204 GetProcessStatus(int pid)
Chris@455 205 {
Chris@455 206 #ifdef _WIN32
Chris@455 207 HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
Chris@455 208 if (!handle) {
Chris@455 209 return ProcessNotRunning;
Chris@455 210 } else {
Chris@455 211 CloseHandle(handle);
Chris@455 212 return ProcessRunning;
Chris@455 213 }
Chris@455 214 #else
Chris@455 215 if (kill(getpid(), 0) == 0) {
Chris@455 216 if (kill(pid, 0) == 0) {
Chris@455 217 return ProcessRunning;
Chris@455 218 } else {
Chris@455 219 return ProcessNotRunning;
Chris@455 220 }
Chris@455 221 } else {
Chris@455 222 return UnknownProcessStatus;
Chris@455 223 }
Chris@455 224 #endif
Chris@455 225 }
Chris@455 226
Chris@79 227 FolderStatus getFolderStatus(QString path)
Chris@79 228 {
Chris@85 229 if (path != "/" && path.endsWith("/")) {
Chris@85 230 path = path.left(path.length()-1);
Chris@85 231 }
Chris@84 232 DEBUG << "getFolderStatus: " << path << endl;
Chris@79 233 QFileInfo fi(path);
Chris@79 234 if (fi.exists()) {
Chris@84 235 DEBUG << "exists" << endl;
Chris@79 236 QDir dir(path);
Chris@79 237 if (!dir.exists()) { // returns false for files
Chris@84 238 DEBUG << "not directory" << endl;
Chris@79 239 return FolderIsFile;
Chris@79 240 }
Chris@79 241 if (QDir(dir.filePath(".hg")).exists()) {
Chris@84 242 DEBUG << "has repo" << endl;
Chris@79 243 return FolderHasRepo;
Chris@79 244 }
Chris@79 245 return FolderExists;
Chris@79 246 } else {
Chris@79 247 QDir parent = fi.dir();
Chris@79 248 if (parent.exists()) {
Chris@84 249 DEBUG << "parent exists" << endl;
Chris@79 250 return FolderParentExists;
Chris@79 251 }
Chris@79 252 return FolderUnknown;
Chris@79 253 }
Chris@79 254 }
Chris@79 255
Chris@79 256 QString getContainingRepoFolder(QString path)
Chris@79 257 {
Chris@79 258 if (getFolderStatus(path) == FolderHasRepo) return "";
Chris@79 259
Chris@79 260 QFileInfo me(path);
Chris@79 261 QFileInfo parent(me.dir().absolutePath());
Chris@79 262
Chris@79 263 while (me != parent) {
Chris@79 264 QString parentPath = parent.filePath();
Chris@79 265 if (getFolderStatus(parentPath) == FolderHasRepo) {
Chris@79 266 return parentPath;
Chris@79 267 }
Chris@79 268 me = parent;
Chris@79 269 parent = me.dir().absolutePath();
Chris@79 270 }
Chris@79 271
Chris@79 272 return "";
Chris@79 273 }
Chris@79 274
Chris@79 275 QString xmlEncode(QString s)
Chris@79 276 {
Chris@79 277 s
Chris@79 278 .replace("&", "&amp;")
Chris@79 279 .replace("<", "&lt;")
Chris@79 280 .replace(">", "&gt;")
Chris@79 281 .replace("\"", "&quot;")
Chris@79 282 .replace("'", "&apos;");
Chris@79 283
Chris@79 284 return s;
Chris@79 285 }
Chris@408 286
Chris@408 287 QString uniDecode(QString s)
Chris@408 288 {
Chris@408 289 QString d;
Chris@408 290 for (int i = 0; i < s.length(); ++i) {
Chris@412 291 // backslash escaped with another backslash: replace with a
Chris@410 292 // single backslash and skip on
Chris@411 293 if (i+1 < s.length() && s[i] == '\\' && s[i+1] == '\\') {
Chris@411 294 d += '\\';
Chris@411 295 i += 1;
Chris@410 296 continue;
Chris@410 297 }
Chris@410 298 // unescaped backslash followed by u and at least four more
Chris@410 299 // chars: replace with Unicode character
Chris@408 300 if (i+5 < s.length() && s[i] == '\\' && s[i+1] == 'u') {
Chris@408 301 QString uni = s.mid(i+2, 4);
Chris@408 302 QByteArray ba = QByteArray::fromHex(uni.toAscii());
Chris@408 303 d += QChar(ba[1], ba[0]);
Chris@408 304 i += 5;
Chris@410 305 continue;
Chris@408 306 }
Chris@410 307 // default case: leave alone
Chris@410 308 d += s[i];
Chris@408 309 }
Chris@408 310 return d;
Chris@408 311 }
Chris@443 312
Chris@443 313 QByteArray randomKey()
Chris@443 314 {
Chris@443 315 static int len = 16;
Chris@443 316
Chris@443 317 QByteArray ba;
Chris@443 318 ba.resize(len);
Chris@443 319 char *buffer = ba.data();
Chris@443 320
Chris@443 321 #ifdef Q_OS_WIN32
Chris@443 322
Chris@443 323 HCRYPTPROV provider = 0;
Chris@443 324 if (!CryptAcquireContextW(&provider, 0, 0,
Chris@443 325 PROV_RSA_FULL,
Chris@443 326 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
Chris@443 327 return QByteArray();
Chris@443 328 }
Chris@443 329
chris@447 330 if (!CryptGenRandom(provider, len, (BYTE *)buffer)) {
Chris@443 331 CryptReleaseContext(provider, 0);
Chris@443 332 return QByteArray();
Chris@443 333 }
Chris@443 334
Chris@443 335 CryptReleaseContext(provider, 0);
Chris@443 336
Chris@443 337 #else
Chris@443 338
Chris@443 339 FILE *rf = fopen("/dev/urandom", "r");
Chris@443 340 if (!rf) {
Chris@443 341 return QByteArray();
Chris@443 342 }
Chris@443 343
Chris@443 344 for (int i = 0; i < len; ++i) {
Chris@443 345 buffer[i] = fgetc(rf);
Chris@443 346 }
Chris@443 347
Chris@443 348 fclose(rf);
Chris@443 349
Chris@443 350 #endif
Chris@443 351
Chris@443 352 return ba;
Chris@443 353 }
Chris@443 354