<commit_before>/*
 * GAMS - General Algebraic Modeling System C++ API
 *
 * Copyright (c) 2017 GAMS Software GmbH <<EMAIL>>
 * Copyright (c) 2017 GAMS Development Corp. <<EMAIL>>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifdef _WIN32
#include "Windows.h"
#endif

#include "gamsplatform.h"
#include <QStringList>
#include <QSettings>
#include <QProcess>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include "gamspath.h"
#include "gamsenum.h"
#include "gamslog.h"
#include "gamsexception.h"

namespace gams {

using namespace std;

#ifdef __linux__ // Linux
const char cEnvSep = ':';
const char *cPathSep = "/";
const char *cCmpFileName = "gmscmpun.txt";
const int cExitCodeDiv = 256;
const char *cExeSuffix = "";
const char *cLibPrefix = "lib";
const char *cLibSuffix = ".so";
const char *cLibEnv = "LD_LIBRARY_PATH";
#elif __APPLE__ // Apple iOS
const char cEnvSep = ':';
const char *cPathSep = "/";
const char *cCmpFileName = "gmscmpun.txt";
const int cExitCodeDiv = 256;
const char *cExeSuffix = "";
const char *cLibPrefix = "lib";
const char *cLibSuffix = ".dylib";
const char *cLibEnv = "DYLD_LIBRARY_PATH";
#elif __unix__ // other *nix
const char cEnvSep = ':';
const char *cPathSep = "/";
const char *cCmpFileName = "gmscmpun.txt";
const int cExitCodeDiv = 256;
const char *cExeSuffix = "";
const char *cLibPrefix = "lib";
const char *cLibSuffix = ".so";
const char *cLibEnv = "LD_LIBRARY_PATH";
#else // Windows
const char cEnvSep = ';';
const char *cLibEnv = "";                   // JM: unused until now
const char *cPathSep = "\\";
const char *cCmpFileName = "gmscmpnt.txt";
const int cExitCodeDiv = 1;
const char *cExeSuffix = ".exe";
const char *cLibPrefix = "";
const char *cLibSuffix = ".dll";
#endif

std::string GAMSPlatform::findGams(LogId logId)
{
#ifdef __linux__
    return findGamsOnLinux(logId);
#elif __APPLE__
    return findGamsOnApple(logId);
#elif __unix__
    return findGamsOnLinux(logId);
#else
    return findGamsOnWindows(logId);
#endif
}

void GAMSPlatform::ensureEnvPathContains(const char *dirName)
{
#ifdef __linux__
    ensureEnvPathSetOnLinux(dirName);
#elif __APPLE__
    ensureEnvPathSetOnApple(dirName);
#elif __unix__
    ensureEnvPathSetOnLinux(dirName);
#else
    ensureEnvPathSetOnWindows(dirName);
#endif
}

bool GAMSPlatform::interrupt(long pid)
{
#ifdef __linux__
    return interruptOnNonWindows(pid);
#elif __APPLE__
    return interruptOnNonWindows(pid);
#elif __unix__
    return interruptOnNonWindows(pid);
#else
    return interruptOnWindows(pid);
#endif
}

std::string GAMSPlatform::findGamsOnLinux(LogId logId)
{
    QString gamsDir;
    QString gamsDirLD;

    QStringList envSplit = QString(qgetenv("PATH")).split(cEnvSep);
    for (QString envStr: envSplit) {
        if ((GAMSPath(envStr) / "gams").exists()) {
            gamsDir = envStr;
            break;
        }
    }
    if (!gamsDir.isEmpty() && LoggerPool::instance().debug(logId) == GAMSEnum::DebugLevel::Off)
        return gamsDir.toStdString();

    envSplit = QString(qgetenv(cLibEnv)).split(cEnvSep);
    for (QString envStr: envSplit) {
        if ((GAMSPath(envStr) / "gams").exists()) {
            gamsDirLD = envStr;
            break;
        }
    }
    if (gamsDir.isEmpty())
        return gamsDirLD.toStdString();
    else if (!gamsDirLD.isEmpty() && gamsDir != gamsDirLD) {
        DEB_S(logId) << "--- Warning: Found GAMS system directory " << gamsDir << " in PATH and a different one" << endl
                     << "---          in (DY)LD_LIBRARY_PATH (" << gamsDirLD << "). The latter is ignored.";
    }

    return gamsDir.toStdString();

}

void GAMSPlatform::ensureEnvPathSetOnLinux(const char *dirName)
{
    Q_UNUSED(dirName)
}

bool GAMSPlatform::interruptOnNonWindows(long pid)
{
    QString childListStr = "";
    QProcess proc;
    proc.setProgram("/bin/bash");
    QStringList s1;
#ifdef __APPLE__
    s1 << "-c" << QString("pstree pid ") + QString::number(pid).toUtf8().constData()
          + QString(" | sed 's/^.*- /(/g' | grep '^(' | cut -f 1 -d ' ' | sed 's/(//g'");
#else

    s1 << "-c" << QString("pstree -p ") + QString::number(pid).toUtf8().constData()
          + QString(" | sed 's/(/\\n(/g' | grep '(' | sed 's/(\\(.*\\)).*/\\1/'");
#endif
    proc.setArguments(QStringList(s1));
    proc.start();
    proc.waitForFinished(-1);
    QString s(proc.readAllStandardOutput());

    QStringList childList = s.split("\n");

    for(int i=0; i<childList.length(); i++)
    {
        childListStr += childList[i].toUtf8().constData();
        childListStr += " ";
    }

    //start "kill" with List of children PID
    proc.setProgram("/bin/bash");
    QStringList s2 { "-c", "kill -2 " + childListStr};
    proc.setArguments(s2);
    proc.start();

    //Note: In C++ we need to wait for QProcess to terminate, otherwise it will be destroyed since it was created on the stack. Should we do the same in C# to make it consistent?
    proc.waitForFinished(-1);
    return true;
}

std::string GAMSPlatform::findGamsOnApple(LogId logId)
{
    QString gamsDir;
    QString gamsDirLD;

    QStringList envSplit = QString(qgetenv("PATH")).split(cEnvSep);
    for (QString envStr: envSplit) {
        if ((GAMSPath(envStr) / "gams").exists()) {
            gamsDir = envStr;
            break;
        }
    }<commit_msg>interrupt on non windows update.<commit_after>/*
 * GAMS - General Algebraic Modeling System C++ API
 *
 * Copyright (c) 2017 GAMS Software GmbH <<EMAIL>>
 * Copyright (c) 2017 GAMS Development Corp. <<EMAIL>>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifdef _WIN32
#include "Windows.h"
#endif

#include "gamsplatform.h"
#include <QStringList>
#include <QSettings>
#include <QProcess>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include "gamspath.h"
#include "gamsenum.h"
#include "gamslog.h"
#include "gamsexception.h"

namespace gams {

using namespace std;

#ifdef __linux__ // Linux
const char cEnvSep = ':';
const char *cPathSep = "/";
const char *cCmpFileName = "gmscmpun.txt";
const int cExitCodeDiv = 256;
const char *cExeSuffix = "";
const char *cLibPrefix = "lib";
const char *cLibSuffix = ".so";
const char *cLibEnv = "LD_LIBRARY_PATH";
#elif __APPLE__ // Apple iOS
const char cEnvSep = ':';
const char *cPathSep = "/";
const char *cCmpFileName = "gmscmpun.txt";
const int cExitCodeDiv = 256;
const char *cExeSuffix = "";
const char *cLibPrefix = "lib";
const char *cLibSuffix = ".dylib";
const char *cLibEnv = "DYLD_LIBRARY_PATH";
#elif __unix__ // other *nix
const char cEnvSep = ':';
const char *cPathSep = "/";
const char *cCmpFileName = "gmscmpun.txt";
const int cExitCodeDiv = 256;
const char *cExeSuffix = "";
const char *cLibPrefix = "lib";
const char *cLibSuffix = ".so";
const char *cLibEnv = "LD_LIBRARY_PATH";
#else // Windows
const char cEnvSep = ';';
const char *cLibEnv = "";                   // JM: unused until now
const char *cPathSep = "\\";
const char *cCmpFileName = "gmscmpnt.txt";
const int cExitCodeDiv = 1;
const char *cExeSuffix = ".exe";
const char *cLibPrefix = "";
const char *cLibSuffix = ".dll";
#endif

std::string GAMSPlatform::findGams(LogId logId)
{
#ifdef __linux__
    return findGamsOnLinux(logId);
#elif __APPLE__
    return findGamsOnApple(logId);
#elif __unix__
    return findGamsOnLinux(logId);
#else
    return findGamsOnWindows(logId);
#endif
}

void GAMSPlatform::ensureEnvPathContains(const char *dirName)
{
#ifdef __linux__
    ensureEnvPathSetOnLinux(dirName);
#elif __APPLE__
    ensureEnvPathSetOnApple(dirName);
#elif __unix__
    ensureEnvPathSetOnLinux(dirName);
#else
    ensureEnvPathSetOnWindows(dirName);
#endif
}

bool GAMSPlatform::interrupt(long pid)
{
#ifdef __linux__
    return interruptOnNonWindows(pid);
#elif __APPLE__
    return interruptOnNonWindows(pid);
#elif __unix__
    return interruptOnNonWindows(pid);
#else
    return interruptOnWindows(pid);
#endif
}

std::string GAMSPlatform::findGamsOnLinux(LogId logId)
{
    QString gamsDir;
    QString gamsDirLD;

    QStringList envSplit = QString(qgetenv("PATH")).split(cEnvSep);
    for (QString envStr: envSplit) {
        if ((GAMSPath(envStr) / "gams").exists()) {
            gamsDir = envStr;
            break;
        }
    }
    if (!gamsDir.isEmpty() && LoggerPool::instance().debug(logId) == GAMSEnum::DebugLevel::Off)
        return gamsDir.toStdString();

    envSplit = QString(qgetenv(cLibEnv)).split(cEnvSep);
    for (QString envStr: envSplit) {
        if ((GAMSPath(envStr) / "gams").exists()) {
            gamsDirLD = envStr;
            break;
        }
    }
    if (gamsDir.isEmpty())
        return gamsDirLD.toStdString();
    else if (!gamsDirLD.isEmpty() && gamsDir != gamsDirLD) {
        DEB_S(logId) << "--- Warning: Found GAMS system directory " << gamsDir << " in PATH and a different one" << endl
                     << "---          in (DY)LD_LIBRARY_PATH (" << gamsDirLD << "). The latter is ignored.";
    }

    return gamsDir.toStdString();

}

void GAMSPlatform::ensureEnvPathSetOnLinux(const char *dirName)
{
    Q_UNUSED(dirName)
}

bool GAMSPlatform::interruptOnNonWindows(long pid)
{
    QProcess proc;
    proc.setProgram("/bin/bash");

    //start "kill" with List of children PID
    proc.setProgram("/bin/bash");
    QStringList s2 { "-c", "kill -2 " + QString::number(pid)};
    proc.setArguments(s2);
    proc.start();

    //Note: In C++ we need to wait for QProcess to terminate, otherwise it will be destroyed since it was created on the stack. Should we do the same in C# to make it consistent?
    proc.waitForFinished(-1);
    return true;
}

std::string GAMSPlatform::findGamsOnApple(LogId logId)
{
    QString gamsDir;
    QString gamsDirLD;

    QStringList envSplit = QString(qgetenv("PATH")).split(cEnvSep);
    for (QString envStr: envSplit) {
        if ((GAMSPath(envStr) / "gams").exists()) {
            gamsDir = envStr;
            break;
        }
    }
{'fraction_non_alphanumeric': 0.12480160387603374, 'fraction_numerical': 0.004928577395372149, 'mean_word_length': 3.6985871271585555, 'pattern_counts': {'":': 0, '<': 62, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5355396', 'n_tokens_mistral': 4030, 'n_tokens_neox': 3862, 'n_words': 1172}