| /* | |
| Copyright (c) 2017 Microsoft Corporation. All rights reserved. | |
| Released under Apache 2.0 license as described in the file LICENSE. | |
| Author: Leonardo de Moura, Gabriel Ebner | |
| */ | |
| #include "util/path.h" | |
| #if defined(LEAN_WINDOWS) | |
| #include <windows.h> | |
| #endif | |
| #include <string> | |
| #include <cstring> | |
| #include <cstdlib> | |
| #include <fstream> | |
| #include <vector> | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include "runtime/exception.h" | |
| #include "runtime/sstream.h" | |
| #include "runtime/optional.h" | |
| #ifdef _MSC_VER | |
| #define S_ISDIR(mode) ((mode & _S_IFDIR) != 0) | |
| #else | |
| #include <dirent.h> | |
| #endif | |
| namespace lean { | |
| file_not_found_exception::file_not_found_exception(std::string const & fname): | |
| exception(sstream() << "file '" << fname << "' not found"), | |
| m_fname(fname) {} | |
| #if defined(LEAN_EMSCRIPTEN) | |
| // emscripten version | |
| static char g_path_sep = ':'; | |
| static constexpr char g_sep = '/'; | |
| static char g_bad_sep = '\\'; | |
| bool is_path_sep(char c) { return c == g_path_sep; } | |
| #elif defined(LEAN_WINDOWS) | |
| // Windows version | |
| static char g_path_sep = ';'; | |
| static constexpr char g_sep = '\\'; | |
| static char g_bad_sep = '/'; | |
| std::string get_exe_location() { | |
| HMODULE hModule = GetModuleHandleW(NULL); | |
| WCHAR path[MAX_PATH]; | |
| GetModuleFileNameW(hModule, path, MAX_PATH); | |
| std::wstring pathstr(path); | |
| return std::string(pathstr.begin(), pathstr.end()); | |
| } | |
| bool is_path_sep(char c) { return c == g_path_sep; } | |
| #elif defined(__APPLE__) | |
| // OSX version | |
| #include <mach-o/dyld.h> | |
| #include <limits.h> | |
| #include <stdlib.h> | |
| static char g_path_sep = ':'; | |
| static constexpr char g_sep = '/'; | |
| static char g_bad_sep = '\\'; | |
| std::string get_exe_location() { | |
| char buf1[PATH_MAX]; | |
| char buf2[PATH_MAX]; | |
| uint32_t bufsize = PATH_MAX; | |
| if (_NSGetExecutablePath(buf1, &bufsize) != 0) | |
| throw exception("failed to locate Lean executable location"); | |
| if (!realpath(buf1, buf2)) | |
| throw exception("failed to resolve symbolic links in " + std::string(buf1)); | |
| return std::string(buf2); | |
| } | |
| bool is_path_sep(char c) { return c == g_path_sep; } | |
| #else | |
| // Linux version | |
| #include <unistd.h> | |
| #include <string.h> | |
| #include <limits.h> // NOLINT | |
| #include <stdio.h> | |
| static char g_path_sep = ':'; | |
| static constexpr char g_sep = '/'; | |
| static char g_bad_sep = '\\'; | |
| std::string get_exe_location() { | |
| char path[PATH_MAX]; | |
| char dest[PATH_MAX]; | |
| memset(dest, 0, PATH_MAX); | |
| pid_t pid = getpid(); | |
| snprintf(path, PATH_MAX, "/proc/%d/exe", pid); | |
| if (readlink(path, dest, PATH_MAX) == -1) { | |
| throw exception("failed to locate Lean executable location"); | |
| } else { | |
| return std::string(dest); | |
| } | |
| } | |
| bool is_path_sep(char c) { return c == g_path_sep; } | |
| #endif | |
| #if defined(LEAN_WINDOWS) | |
| std::string resolve(std::string const & rel_or_abs, std::string const & base) { | |
| // TODO(gabriel): detect absolute pathnames | |
| return base + g_sep + rel_or_abs; | |
| } | |
| #else | |
| std::string resolve(std::string const & rel_or_abs, std::string const & base) { | |
| if (!rel_or_abs.empty() && rel_or_abs[0] == g_sep) { | |
| // absolute | |
| return rel_or_abs; | |
| } else { | |
| // relative | |
| return base + g_sep + rel_or_abs; | |
| } | |
| } | |
| #endif | |
| std::string normalize_path(std::string f) { | |
| for (auto & c : f) { | |
| if (c == g_bad_sep) | |
| c = g_sep; | |
| } | |
| return f; | |
| } | |
| std::string get_path(std::string f) { | |
| while (true) { | |
| if (f.empty()) | |
| throw exception("failed to locate Lean executable location"); | |
| if (f.back() == g_sep) { | |
| f.pop_back(); | |
| return f; | |
| } | |
| f.pop_back(); | |
| } | |
| } | |
| static char g_sep_str[2] = { g_sep, 0 }; | |
| char const * get_dir_sep() { return g_sep_str; } | |
| char get_dir_sep_ch() { return g_sep; } | |
| bool has_file_ext(std::string const & fname, char const * ext) { | |
| unsigned ext_len = strlen(ext); | |
| return fname.size() > ext_len && fname.substr(fname.size() - ext_len, ext_len) == ext; | |
| } | |
| void find_files(std::string const & base, char const * ext, std::vector<std::string> & files) { | |
| for (auto & fn : read_dir(base)) { | |
| if (auto i_d = is_dir(fn)) { | |
| if (*i_d) { | |
| find_files(fn, ext, files); | |
| } else if (has_file_ext(fn, ext)) { | |
| files.push_back(fn); | |
| } | |
| } | |
| } | |
| } | |
| std::string dirname(std::string const & fname) { | |
| auto nfname = normalize_path(fname); | |
| auto i = nfname.rfind(g_sep); | |
| if (i == std::string::npos) { | |
| return "."; | |
| } else { | |
| return nfname.substr(0, i); | |
| } | |
| } | |
| std::string stem(std::string const & fname) { | |
| auto nfname = normalize_path(fname); | |
| auto i = nfname.rfind(g_sep); | |
| if (i == std::string::npos) { | |
| i = 0; | |
| } else { | |
| i++; | |
| } | |
| auto j = nfname.rfind("."); | |
| if (j == std::string::npos) { | |
| j = nfname.size(); | |
| } | |
| return nfname.substr(i, j - i); | |
| } | |
| std::string read_file(std::string const & fname, std::ios_base::openmode mode) { | |
| std::ifstream in(fname, mode); | |
| if (!in.good()) throw file_not_found_exception(fname); | |
| std::stringstream buf; | |
| buf << in.rdbuf(); | |
| return buf.str(); | |
| } | |
| time_t get_mtime(std::string const &fname) { | |
| struct stat st; | |
| if (stat(fname.c_str(), &st) != 0) | |
| return -1; | |
| return st.st_mtime; | |
| } | |
| optional<bool> is_dir(std::string const & fn) { | |
| struct stat st; | |
| if (stat(fn.c_str(), &st) == 0) | |
| return optional<bool>(S_ISDIR(st.st_mode)); | |
| return optional<bool>(); | |
| } | |
| bool is_directory(std::string const & fn) { | |
| if (auto res = is_dir(fn)) { | |
| return *res; | |
| } else { | |
| return false; | |
| } | |
| } | |
| std::vector<std::string> read_dir(std::string const &dirname) { | |
| std::vector<std::string> files; | |
| #ifdef _MSC_VER | |
| WIN32_FIND_DATA data; | |
| std::string dir = dirname + "\\*"; | |
| HANDLE hFind = FindFirstFile(dir.c_str(), &data); | |
| if (hFind != INVALID_HANDLE_VALUE) { | |
| do { | |
| if (strcmp(data.cFileName, ".") != 0 && strcmp(data.cFileName, "..") != 0) | |
| files.push_back(dirname + '\\' + data.cFileName); | |
| } while (FindNextFile(hFind, &data)); | |
| FindClose(hFind); | |
| } | |
| #else | |
| auto dir = opendir(dirname.c_str()); | |
| if (!dir) throw exception(sstream() << "could not open directory " << dirname << ": " << std::strerror(errno)); | |
| while (auto ep = readdir(dir)) { // NOLINT | |
| // ^^ disabling readdir_r lint because glibc recommends using readdir now. | |
| std::string fn = ep->d_name; | |
| if (fn == "." || fn == "..") continue; | |
| files.push_back(dirname + get_dir_sep() + fn); | |
| } | |
| closedir(dir); | |
| #endif | |
| return files; | |
| } | |
| std::string lrealpath(std::string const & fname) { | |
| #if defined(LEAN_EMSCRIPTEN) | |
| return fname; | |
| #elif defined(LEAN_WINDOWS) | |
| constexpr unsigned BufferSize = 8192; | |
| char buffer[BufferSize]; | |
| DWORD retval = GetFullPathName(fname.c_str(), BufferSize, buffer, nullptr); | |
| if (retval == 0 || retval > BufferSize) { | |
| return fname; | |
| } else { | |
| return std::string(buffer); | |
| } | |
| #else | |
| constexpr unsigned BufferSize = 8192; | |
| char buffer[BufferSize]; | |
| char * tmp = realpath(fname.c_str(), buffer); | |
| if (tmp) { | |
| std::string r(tmp); | |
| return r; | |
| } else { | |
| throw file_not_found_exception(fname); | |
| } | |
| #endif | |
| } | |
| } |