packages feed

souffle-haskell-2.1.0: cbits/souffle/utility/FileUtil.h

/*
 * Souffle - A Datalog Compiler
 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved
 * Licensed under the Universal Permissive License v 1.0 as shown at:
 * - https://opensource.org/licenses/UPL
 * - <souffle root>/licenses/SOUFFLE-UPL.txt
 */

/************************************************************************
 *
 * @file FileUtil.h
 *
 * @brief Datalog project utilities
 *
 ***********************************************************************/

#pragma once

#include <climits>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <string>
#include <utility>
#include <sys/stat.h>

#ifndef _WIN32
#include <unistd.h>
#else
#include <fcntl.h>
#include <io.h>
#include <stdlib.h>
#include <windows.h>

// -------------------------------------------------------------------------------
//                               File Utils
// -------------------------------------------------------------------------------

#define X_OK 1 /* execute permission - unsupported in windows*/

#define PATH_MAX 260

/**
 * access and realpath are missing on windows, we use their windows equivalents
 * as work-arounds.
 */
#define access _access
inline char* realpath(const char* path, char* resolved_path) {
    return _fullpath(resolved_path, path, PATH_MAX);
}

/**
 * Define an alias for the popen and pclose functions on windows
 */
#define popen _popen
#define pclose _pclose
#endif

namespace souffle {

/**
 *  Check whether a file exists in the file system
 */
inline bool existFile(const std::string& name) {
    struct stat buffer = {};
    if (stat(name.c_str(), &buffer) == 0) {
        if ((buffer.st_mode & S_IFMT) != 0) {
            return true;
        }
    }
    return false;
}

/**
 *  Check whether a directory exists in the file system
 */
inline bool existDir(const std::string& name) {
    struct stat buffer = {};
    if (stat(name.c_str(), &buffer) == 0) {
        if ((buffer.st_mode & S_IFDIR) != 0) {
            return true;
        }
    }
    return false;
}

/**
 * Check whether a given file exists and it is an executable
 */
inline bool isExecutable(const std::string& name) {
    return existFile(name) && (access(name.c_str(), X_OK) == 0);
}

/**
 * Simple implementation of a which tool
 */
inline std::string which(const std::string& name) {
    // Check if name has path components in it and if so return it immediately
    if (name.find('/') != std::string::npos) {
        return name;
    }
    // Get PATH from environment, if it exists.
    const char* syspath = ::getenv("PATH");
    if (syspath == nullptr) {
        return "";
    }
    char buf[PATH_MAX];
    std::stringstream sstr;
    sstr << syspath;
    std::string sub;

    // Check for existence of a binary called 'name' in PATH
    while (std::getline(sstr, sub, ':')) {
        std::string path = sub + "/" + name;
        if ((::realpath(path.c_str(), buf) != nullptr) && isExecutable(path) && !existDir(path)) {
            return buf;
        }
    }
    return "";
}

/**
 *  C++-style dirname
 */
inline std::string dirName(const std::string& name) {
    if (name.empty()) {
        return ".";
    }
    size_t lastNotSlash = name.find_last_not_of('/');
    // All '/'
    if (lastNotSlash == std::string::npos) {
        return "/";
    }
    size_t leadingSlash = name.find_last_of('/', lastNotSlash);
    // No '/'
    if (leadingSlash == std::string::npos) {
        return ".";
    }
    // dirname is '/'
    if (leadingSlash == 0) {
        return "/";
    }
    return name.substr(0, leadingSlash);
}

/**
 *  C++-style realpath
 */
inline std::string absPath(const std::string& path) {
    char buf[PATH_MAX];
    char* res = realpath(path.c_str(), buf);
    return (res == nullptr) ? "" : std::string(buf);
}

/**
 *  Join two paths together; note that this does not resolve overlaps or relative paths.
 */
inline std::string pathJoin(const std::string& first, const std::string& second) {
    unsigned firstPos = static_cast<unsigned>(first.size()) - 1;
    while (first.at(firstPos) == '/') {
        firstPos--;
    }
    unsigned secondPos = 0;
    while (second.at(secondPos) == '/') {
        secondPos++;
    }
    return first.substr(0, firstPos + 1) + '/' + second.substr(secondPos);
}

/*
 * Find out if an executable given by @p tool exists in the path given @p path
 * relative to the directory given by @ base. A path here refers a
 * colon-separated list of directories.
 */
inline std::string findTool(const std::string& tool, const std::string& base, const std::string& path) {
    std::string dir = dirName(base);
    std::stringstream sstr(path);
    std::string sub;

    while (std::getline(sstr, sub, ':')) {
        std::string subpath = dir + "/" + sub + '/' + tool;
        if (isExecutable(subpath)) {
            return absPath(subpath);
        }
    }
    return "";
}

/*
 * Get the basename of a fully qualified filename
 */
inline std::string baseName(const std::string& filename) {
    if (filename.empty()) {
        return ".";
    }

    size_t lastNotSlash = filename.find_last_not_of('/');
    if (lastNotSlash == std::string::npos) {
        return "/";
    }

    size_t lastSlashBeforeBasename = filename.find_last_of('/', lastNotSlash - 1);
    if (lastSlashBeforeBasename == std::string::npos) {
        lastSlashBeforeBasename = static_cast<size_t>(-1);
    }
    return filename.substr(lastSlashBeforeBasename + 1, lastNotSlash - lastSlashBeforeBasename);
}

/**
 * File name, with extension removed.
 */
inline std::string simpleName(const std::string& path) {
    std::string name = baseName(path);
    const size_t lastDot = name.find_last_of('.');
    // file has no extension
    if (lastDot == std::string::npos) {
        return name;
    }
    const size_t lastSlash = name.find_last_of('/');
    // last slash occurs after last dot, so no extension
    if (lastSlash != std::string::npos && lastSlash > lastDot) {
        return name;
    }
    // last dot after last slash, or no slash
    return name.substr(0, lastDot);
}

/**
 * File extension, with all else removed.
 */
inline std::string fileExtension(const std::string& path) {
    std::string name = path;
    const size_t lastDot = name.find_last_of('.');
    // file has no extension
    if (lastDot == std::string::npos) {
        return std::string();
    }
    const size_t lastSlash = name.find_last_of('/');
    // last slash occurs after last dot, so no extension
    if (lastSlash != std::string::npos && lastSlash > lastDot) {
        return std::string();
    }
    // last dot after last slash, or no slash
    return name.substr(lastDot + 1);
}

/**
 * Generate temporary file.
 */
inline std::string tempFile() {
#ifdef _WIN32
    std::string templ;
    std::FILE* f = nullptr;
    while (f == nullptr) {
        templ = std::tmpnam(nullptr);
        f = fopen(templ.c_str(), "wx");
    }
    fclose(f);
    return templ;
#else
    char templ[40] = "./souffleXXXXXX";
    close(mkstemp(templ));
    return std::string(templ);
#endif
}

inline std::stringstream execStdOut(char const* cmd) {
    FILE* in = popen(cmd, "r");
    std::stringstream data;
    while (in != nullptr) {
        int c = fgetc(in);
        if (feof(in) != 0) {
            break;
        }
        data << static_cast<char>(c);
    }
    pclose(in);
    return data;
}

inline std::stringstream execStdOut(std::string const& cmd) {
    return execStdOut(cmd.c_str());
}

class TempFileStream : public std::fstream {
    std::string fileName;

public:
    TempFileStream(std::string fileName = tempFile())
            : std::fstream(fileName), fileName(std::move(fileName)) {}
    ~TempFileStream() override {
        close();
        remove(fileName.c_str());
    }

    std::string const& getFileName() const {
        return fileName;
    }
};

}  // namespace souffle