view src/common.cpp @ 689:af295de6a59b

Ensure debug log is always created, in %AppData%
author Chris Cannam
date Mon, 10 Dec 2018 10:28:15 +0000
parents f9b805d8cab4
children c0b46d0514a7
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

/*
    EasyMercurial

    Based on HgExplorer by Jari Korhonen
    Copyright (c) 2010 Jari Korhonen
    Copyright (c) 2013 Chris Cannam
    Copyright (c) 2013 Queen Mary, University of London
    
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/

#include "common.h"
#include "debug.h"

#include <QFileInfo>
#include <QProcessEnvironment>
#include <QStringList>
#include <QDir>
#include <QRegExp>

#include <sys/types.h>

#ifdef Q_OS_WIN32
#define _WIN32_WINNT 0x0500
#define SECURITY_WIN32 
#include <windows.h>
#include <security.h>
#include <process.h>
#else
#include <errno.h>
#include <pwd.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#endif

QString findInPath(QString name, QString installPath, bool executableRequired)
{
    bool found = false;
    if (name != "") {
        if (name[0] != '/'
#ifdef Q_OS_WIN32
            && (QRegExp("^\\w:").indexIn(name) != 0)
#endif
            ) {
#ifdef Q_OS_WIN32
            QChar pathSep = ';';
#else
            QChar pathSep = ':';
#endif
            name = QFileInfo(name).fileName();
            QString path =
                QProcessEnvironment::systemEnvironment().value("PATH");
            DEBUG << "findInPath: seeking location for binary " << name
                  << ": system path is " << path << endl;
            if (installPath != "") {   
                DEBUG << "findInPath: install path is " << installPath
                      << ", adding to system path" << endl;
                //!!! path = path + pathSep + installPath;
                path = installPath + pathSep + path;
            }
#ifndef Q_OS_WIN32
            //!!!
            path = path + ":/usr/local/bin";
            DEBUG << "... adding /usr/local/bin just in case (fix and add settings dlg please)"
                    << endl;
#endif
            QStringList elements = path.split(pathSep, QString::SkipEmptyParts);
            foreach (QString element, elements) {
                QString full = QDir(element).filePath(name);
                QFileInfo fi(full);
                DEBUG << "findInPath: looking at " << full << endl;
                if (fi.exists() && fi.isFile()) {
                    DEBUG << "findInPath: it's a file" << endl;
                    if (!executableRequired || fi.isExecutable()) {
                        name = full;
                        DEBUG << "findInPath: found at " << name << endl;
                        found = true;
                        break;
                    }
                }
            }
        } else {
            // absolute path given
            QFileInfo fi(name);
            DEBUG << "findInPath: looking at absolute path " << name << endl;
            if (fi.exists() && fi.isFile()) {
                DEBUG << "findInPath: it's a file" << endl;
                if (!executableRequired || fi.isExecutable()) {
                    DEBUG << "findInPath: found at " << name << endl;
                    found = true;
                }
            }
        }
    }
#ifdef Q_OS_WIN32
    if (!found) {
        if (!name.endsWith(".exe")) {
            return findInPath(name + ".exe", installPath, executableRequired);
        }
    }
#endif
    if (found) {
        return name;
    } else {
        return "";
    }
}

#ifdef Q_OS_WIN32
QString getUserRealName()
{
    TCHAR buf[1024];
    long unsigned int maxlen = 1000;
    LPTSTR info = buf;

    if (!GetUserNameEx(NameDisplay, info, &maxlen)) {
        DEBUG << "GetUserNameEx failed: " << GetLastError() << endl;
        return "";
    }

#ifdef UNICODE
    return QString::fromUtf16((const unsigned short *)info);
#else
    return QString::fromLocal8Bit(info);
#endif
}
#else
#ifdef Q_OS_MAC
// Nothing here: definition is in common_osx.mm
#else
QString getUserRealName()
{
    const int maxlen = 1023;
    char buf[maxlen + 2];

    if (getlogin_r(buf, maxlen)) return "";

    struct passwd *p = getpwnam(buf);
    if (!p) return "";
    
    QString s(p->pw_gecos);
    if (s != "") s = s.split(',')[0];
    return s;
}
#endif
#endif

void loseControllingTerminal()
{
#ifndef Q_OS_WIN32

    if (!isatty(0)) {
        DEBUG << "stdin is not a terminal" << endl;
    } else {
        DEBUG << "stdin is a terminal, detaching from it" << endl;
        if (ioctl(0, TIOCNOTTY, NULL) < 0) {
            perror("ioctl failed");
            DEBUG << "ioctl for TIOCNOTTY on stdin failed (errno = " << errno << ")" << endl;
        } else {
            DEBUG << "ioctl for TIOCNOTTY on stdin succeeded" << endl;
	    return;
        }
    }

    int ttyfd = open("/dev/tty", O_RDWR);
    if (ttyfd < 0) {
        DEBUG << "failed to open controlling terminal" << endl;
    } else {
        if (ioctl(ttyfd, TIOCNOTTY, NULL) < 0) {
            perror("ioctl failed");
            DEBUG << "ioctl for TIOCNOTTY on controlling terminal failed (errno = " << errno << ")" << endl;
        } else {
            DEBUG << "ioctl for TIOCNOTTY on controlling terminal succeeded" << endl;
	    return;
        }
    }

#endif
}

void installSignalHandlers()
{
#ifndef Q_OS_WIN32
    sigset_t sgnals;
    sigemptyset (&sgnals);
    sigaddset(&sgnals, SIGHUP);
    sigaddset(&sgnals, SIGCONT);
    pthread_sigmask(SIG_BLOCK, &sgnals, 0);
#endif
}

ProcessStatus
GetProcessStatus(int pid)
{
#ifdef _WIN32
    HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (!handle) {
        return ProcessNotRunning;
    } else {
        CloseHandle(handle);
        return ProcessRunning;
    }
#else
    if (kill(getpid(), 0) == 0) {
        if (kill(pid, 0) == 0) {
            return ProcessRunning;
        } else {
            return ProcessNotRunning;
        }
    } else {
        return UnknownProcessStatus;
    }
#endif
}

FolderStatus getFolderStatus(QString path)
{
    if (path != "/" && path.endsWith("/")) {
        path = path.left(path.length()-1);
    }
    DEBUG << "getFolderStatus: " << path << endl;
    QFileInfo fi(path);
    if (fi.exists()) {
        DEBUG << "exists" << endl;
        QDir dir(path);
        if (!dir.exists()) { // returns false for files
            DEBUG << "not directory" << endl;
            return FolderIsFile;
        }
        if (QDir(dir.filePath(".hg")).exists()) {
            DEBUG << "has repo" << endl;
            return FolderHasRepo;
        }
        return FolderExists;
    } else {
        QDir parent = fi.dir();
        if (parent.exists()) {
            DEBUG << "parent exists" << endl;
            return FolderParentExists;
        }
        return FolderUnknown;
    }
}

QString getContainingRepoFolder(QString path)
{
    if (getFolderStatus(path) == FolderHasRepo) return "";

    QFileInfo me(path);
    QFileInfo parent(me.dir().absolutePath());

    while (me != parent) {
        QString parentPath = parent.filePath();
        if (getFolderStatus(parentPath) == FolderHasRepo) {
            return parentPath;
        }
        me = parent;
        parent = me.dir().absolutePath();
    }

    return "";
}

QString xmlEncode(QString s)
{
    s
	.replace("&", "&amp;")
	.replace("<", "&lt;")
	.replace(">", "&gt;")
	.replace("\"", "&quot;")
	.replace("'", "&apos;");

    return s;
}

QString uniDecode(QString s)
{
    QString d;
    for (int i = 0; i < s.length(); ++i) {
        // backslash escaped with another backslash: replace with a
        // single backslash and skip on
        if (i+1 < s.length() && s[i] == '\\' && s[i+1] == '\\') {
            d += '\\';
            i += 1;
            continue;
        }
        // unescaped backslash followed by u and at least four more
        // chars: replace with Unicode character
        if (i+5 < s.length() && s[i] == '\\' && s[i+1] == 'u') {
            QString uni = s.mid(i+2, 4);
            QByteArray ba = QByteArray::fromHex(uni.toLatin1());
            d += QChar(ba[1], ba[0]);
            i += 5;
            continue;
        }
        // default case: leave alone
        d += s[i];
    }
    return d;
}

QByteArray randomKey()
{
    static int len = 16;

    QByteArray ba;
    ba.resize(len);
    char *buffer = ba.data();

#ifdef Q_OS_WIN32

    HCRYPTPROV provider = 0;
    if (!CryptAcquireContextW(&provider, 0, 0,
                              PROV_RSA_FULL,
                              CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
        return QByteArray();
    }

    if (!CryptGenRandom(provider, len, (BYTE *)buffer)) {
        CryptReleaseContext(provider, 0);
        return QByteArray();
    }

    CryptReleaseContext(provider, 0);

#else
    
    FILE *rf = fopen("/dev/urandom", "r");
    if (!rf) {
        return QByteArray();
    }

    for (int i = 0; i < len; ++i) {
        buffer[i] = fgetc(rf);
    }

    fclose(rf);

#endif

    return ba;
}