Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
20 kB · 763 lines
C++
at develop
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* * Main authors: * Guido Tack <guido.tack@monash.edu> */
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef _MSC_VER#define _CRT_SECURE_NO_WARNINGS#endif
#include <minizinc/_thirdparty/b64/decode.h>#include <minizinc/_thirdparty/b64/encode.h>#include <minizinc/_thirdparty/miniz.h>#include <minizinc/config.hh>#include <minizinc/exception.hh>#include <minizinc/file_utils.hh>
#include <cstring>#include <sstream>#include <string>
#ifdef HAS_PIDPATH#include <cstdio>#include <cstdlib>#include <cstring>#include <libproc.h>#include <unistd.h>#elif defined(HAS_GETMODULEFILENAME) || defined(HAS_GETFILEATTRIBUTES)#define NOMINMAX // Ensure the words min/max remain available#include <windows.h>#undef ERROR#else#include <unistd.h>#endif#include <sys/stat.h>#include <sys/types.h>
#ifdef _MSC_VER#include "Shlwapi.h"#pragma comment(lib, "Shlwapi.lib")#include "Shlobj.h"
#include <direct.h>#else#include <dirent.h>#include <ftw.h>#include <libgen.h>#endif
namespace MiniZinc {namespace FileUtils {
#ifdef HAS_PIDPATHstd::string progpath() { pid_t pid = getpid(); char path[PROC_PIDPATHINFO_MAXSIZE]; int ret = proc_pidpath(pid, path, sizeof(path)); if (ret <= 0) { return ""; } std::string p(path); size_t slash = p.find_last_of('/'); if (slash != std::string::npos) { p = p.substr(0, slash); } return p;}#elif defined(HAS_GETMODULEFILENAME)std::string progpath() { wchar_t path[MAX_PATH]; int ret = GetModuleFileNameW(nullptr, path, MAX_PATH); if (ret <= 0) { return ""; } std::string p = wide_to_utf8(path); size_t slash = p.find_last_of("/\\"); if (slash != std::string::npos) { p = p.substr(0, slash); } return p;}#elsestd::string progpath() { const int bufsz = 2000; char path[bufsz + 1]; ssize_t sz = readlink("/proc/self/exe", path, bufsz); if (sz < 0) { return ""; } path[sz] = '\0'; std::string p(path); size_t slash = p.find_last_of('/'); if (slash != std::string::npos) { p = p.substr(0, slash); } return p;}#endif
bool file_exists(const std::string& filename) {#if defined(HAS_GETFILEATTRIBUTES) DWORD dwAttrib = GetFileAttributesW(utf8_to_wide(filename).c_str());
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0;#else struct stat info; return stat(filename.c_str(), &info) == 0 && ((info.st_mode & S_IFREG) != 0);#endif}
bool directory_exists(const std::string& dirname) {#if defined(HAS_GETFILEATTRIBUTES) DWORD dwAttrib = GetFileAttributesW(utf8_to_wide(dirname).c_str());
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0;#else struct stat info; return stat(dirname.c_str(), &info) == 0 && ((info.st_mode & S_IFDIR) != 0);#endif}
std::string file_path(const std::string& filename, const std::string& basePath) {#ifdef _MSC_VER LPWSTR lpFilePart; DWORD nBufferLength = GetFullPathNameW(utf8_to_wide(filename).c_str(), 0, nullptr, &lpFilePart); auto lpBuffer = static_cast<LPWSTR>(LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * nBufferLength)); if (lpBuffer == nullptr) { return ""; }
std::string ret; DWORD error = GetFullPathNameW(utf8_to_wide(filename).c_str(), nBufferLength, lpBuffer, &lpFilePart); DWORD fileAttr = GetFileAttributesW(lpBuffer); DWORD lastError = GetLastError();
if (error == 0 || (fileAttr == INVALID_FILE_ATTRIBUTES && lastError != NO_ERROR)) { ret = basePath.empty() ? filename : file_path(basePath + "/" + filename); } else { ret = wide_to_utf8(lpBuffer); } LocalFree(lpBuffer); return ret;#else char* rp = realpath(filename.c_str(), nullptr); if (rp == nullptr) { if (basePath.empty()) { return filename; } return file_path(basePath + "/" + filename); } std::string rp_s(rp); free(rp); return rp_s;#endif}
std::string dir_name(const std::string& filename) {#ifdef _MSC_VER size_t pos = filename.find_last_of("\\/"); return (pos == std::string::npos) ? "" : filename.substr(0, pos);#else char* fn = strdup(filename.c_str()); char* dn = dirname(fn); std::string ret(dn); free(fn); return ret;#endif}
std::string base_name(const std::string& filename) {#ifdef _MSC_VER size_t pos = filename.find_last_of("\\/"); return (pos == std::string::npos) ? filename : filename.substr(pos + 1);#else char* fn = strdup(filename.c_str()); char* dn = basename(fn); std::string ret(dn); free(fn); return ret;#endif}
bool is_absolute(const std::string& path) {#ifdef _MSC_VER if (path.size() > 2 && ((path[0] == '\\' && path[1] == '\\') || (path[0] == '/' && path[1] == '/'))) { return true; } return PathIsRelativeW(utf8_to_wide(path).c_str()) == FALSE;#else return path.empty() ? false : (path[0] == '/');#endif}
std::string find_executable(const std::string& filename) { if (is_absolute(filename)) { if (file_exists(filename)) { return filename; }#ifdef _MSC_VER if (FileUtils::file_exists(filename + ".exe")) { return filename + ".exe"; } if (FileUtils::file_exists(filename + ".bat")) { return filename + ".bat"; }#endif return ""; } char* path_c = getenv("PATH");#ifdef _MSC_VER char pathsep = ';';#else char pathsep = ':';#endif std::string path; if (path_c != nullptr) { path = path_c; if (!path.empty()) { path += pathsep; } } path += progpath(); std::string pathItem; std::stringstream pathStream(path); while (std::getline(pathStream, pathItem, pathsep)) { std::string fileWithPath = pathItem.append("/").append(filename); if (file_exists(fileWithPath)) { return fileWithPath; }#ifdef _MSC_VER if (FileUtils::file_exists(fileWithPath + ".exe")) { return fileWithPath + ".exe"; } if (FileUtils::file_exists(fileWithPath + ".bat")) { return fileWithPath + ".bat"; }#endif } return "";}
std::vector<std::string> directory_list(const std::string& dir, const std::string& ext) { std::vector<std::string> entries;#ifdef _MSC_VER WIN32_FIND_DATAW findData; HANDLE hFind = ::FindFirstFileW(utf8_to_wide(dir + "/*." + ext).c_str(), &findData); if (hFind != INVALID_HANDLE_VALUE) { do { if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { entries.push_back(wide_to_utf8(findData.cFileName)); } } while (::FindNextFileW(hFind, &findData) == TRUE); ::FindClose(hFind); }#else DIR* dirp = opendir(dir.c_str()); if (dirp != nullptr) { struct dirent* dp; while ((dp = readdir(dirp)) != nullptr) { std::string fileName(dp->d_name); struct stat info; if (stat(((dir + "/").append(fileName)).c_str(), &info) == 0 && ((info.st_mode & S_IFREG) != 0)) { if (ext == "*") { entries.push_back(fileName); } else { if (fileName.size() > ext.size() + 2 && fileName.substr(fileName.size() - ext.size() - 1) == "." + ext) { entries.push_back(fileName); } } } } closedir(dirp); }#endif return entries;}
std::string working_directory() {#ifdef _MSC_VER wchar_t wd[FILENAME_MAX]; if (_wgetcwd(wd, FILENAME_MAX) == FALSE) { return ""; } return wide_to_utf8(wd);#else char wd[FILENAME_MAX]; if (getcwd(wd, sizeof(wd)) == nullptr) { return ""; } return wd;#endif}
std::string share_directory() {#ifdef _WIN32 if (wchar_t* MZNSTDLIBDIR = _wgetenv(L"MZN_STDLIB_DIR")) { return wide_to_utf8(MZNSTDLIBDIR); }#else if (char* MZNSTDLIBDIR = getenv("MZN_STDLIB_DIR")) { return std::string(MZNSTDLIBDIR); }#endif // NOLINTNEXTLINE(readability-redundant-string-init) std::string static_stdlib_dir(MZN_STATIC_STDLIB_DIR); if (FileUtils::file_exists(static_stdlib_dir + "/std/stdlib.mzn")) { return static_stdlib_dir; } std::string mypath = FileUtils::progpath(); int depth = 0; for (char i : mypath) { if (i == '/' || i == '\\') { depth++; } } for (int i = 0; i <= depth; i++) { if (FileUtils::file_exists(mypath + "/share/minizinc/std/stdlib.mzn")) { return mypath + "/share/minizinc"; } mypath += "/.."; } return "";}
std::string user_config_dir() {#ifdef _MSC_VER HRESULT hr; PWSTR pszPath = nullptr;
hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pszPath); if (SUCCEEDED(hr)) { auto configPath = wide_to_utf8(pszPath); CoTaskMemFree(pszPath); if (configPath.empty()) { return ""; } return configPath + "/MiniZinc"; } return "";#else if (const char* hd = getenv("HOME")) { return std::string(hd) + "/.minizinc"; } return "";#endif}
std::string global_config_file() { std::string sd = share_directory(); if (sd.empty()) { return ""; } return sd + "/Preferences.json";}
std::string user_config_file() { return user_config_dir() + "/Preferences.json"; }
TmpFile::TmpFile(const std::string& ext) {#ifdef _WIN32 WCHAR szTempFileName[MAX_PATH]; WCHAR lpTempPathBuffer[MAX_PATH];
bool didCopy; do { GetTempPathW(MAX_PATH, lpTempPathBuffer); GetTempFileNameW(lpTempPathBuffer, L"tmp_mzn_", 0, szTempFileName);
_name = wide_to_utf8(szTempFileName); _tmpNames.push_back(_name); didCopy = CopyFileW(szTempFileName, utf8_to_wide(_name + ext).c_str(), TRUE) == TRUE; } while (!didCopy); _name += ext;#else _tmpfileDesc = -1; _name = "/tmp/mznfileXXXXXX" + ext; char* tmpfile = strndup(_name.c_str(), _name.size()); _tmpfileDesc = mkstemps(tmpfile, ext.size()); if (_tmpfileDesc == -1) { ::free(tmpfile); throw InternalError("Error occurred when creating temporary file"); } _name = std::string(tmpfile); ::free(tmpfile);#endif}
TmpFile::~TmpFile() {#ifdef _WIN32 _wremove(utf8_to_wide(_name).c_str()); // TODO: Is this necessary? for (auto& n : _tmpNames) { _wremove(utf8_to_wide(n).c_str()); }#else remove(_name.c_str()); if (_tmpfileDesc != -1) { close(_tmpfileDesc); }#endif}
TmpDir::TmpDir() {#ifdef _WIN32 WCHAR szTempFileName[MAX_PATH]; WCHAR lpTempPathBuffer[MAX_PATH];
GetTempPathW(MAX_PATH, lpTempPathBuffer); GetTempFileNameW(lpTempPathBuffer, L"tmp_mzn_", 0, szTempFileName);
_name = wide_to_utf8(szTempFileName); DeleteFileW(szTempFileName); CreateDirectoryW(szTempFileName, nullptr);#else _name = "/tmp/mzndirXXXXXX"; char* tmpfile = strndup(_name.c_str(), _name.size());
if (mkdtemp(tmpfile) == nullptr) { ::free(tmpfile); throw InternalError("Error occurred when creating temporary directory"); } _name = std::string(tmpfile); ::free(tmpfile);#endif}
#ifdef _WIN32namespace {void remove_dir(const std::string& d) { HANDLE dh; WIN32_FIND_DATAW info; auto dw = utf8_to_wide(d); auto pattern = dw + L"\\*.*"; dh = ::FindFirstFileW(pattern.c_str(), &info); if (dh != INVALID_HANDLE_VALUE) { do { if (info.cFileName[0] != L'.') { auto fp = dw + L"\\" + info.cFileName; if ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { remove_dir(wide_to_utf8(fp)); } else { ::SetFileAttributesW(fp.c_str(), FILE_ATTRIBUTE_NORMAL); ::DeleteFileW(fp.c_str()); } } } while (::FindNextFileW(dh, &info) == TRUE); } ::FindClose(dh); ::SetFileAttributesW(dw.c_str(), FILE_ATTRIBUTE_NORMAL); ::RemoveDirectoryW(dw.c_str());}} // namespace#elsenamespace {int remove_file(const char* fpath, const struct stat* /*s*/, int /*i*/, struct FTW* /*ftw*/) { return unlink(fpath);}} // namespace#endif
TmpDir::~TmpDir() {#ifdef _WIN32 remove_dir(_name);#else nftw(_name.c_str(), remove_file, 64, FTW_DEPTH | FTW_PHYS); rmdir(_name.c_str());#endif}
std::vector<std::string> parse_cmd_line(const std::string& s) { // Break the string up at whitespace, except inside quotes, but ignore escaped quotes std::vector<std::string> c; size_t cur = 0; size_t l = s.length(); std::ostringstream oss; bool inside_quote = false; bool had_escape = false; for (; cur < l; cur++) { if (inside_quote) { if (s[cur] == '"') { if (had_escape) { oss << "\""; had_escape = false; } else { inside_quote = false; } } else if (s[cur] == '\\') { had_escape = true; } else { if (had_escape) { oss << "\\"; had_escape = false; } oss << s[cur]; } } else { if (s[cur] == ' ') { if (had_escape) { oss << " "; had_escape = false; } else { c.push_back(oss.str()); oss.str(std::string()); } } else if (s[cur] == '\\') { if (had_escape) { oss << "\\"; had_escape = false; } else { had_escape = true; } } else if (s[cur] == '"') { if (had_escape) { oss << "\""; had_escape = false; } else { inside_quote = true; } } else { if (had_escape) { switch (s[cur]) { case 'a': oss << "\a"; break; case 'b': oss << "\b"; break; case 'f': oss << "\f"; break; case 'n': oss << "\n"; break; case 'r': oss << "\r"; break; case 't': oss << "\t"; break; case 'v': oss << "\v"; break; default: oss << "\\" << s[cur]; break; } had_escape = false; } else { oss << s[cur]; } } } } c.push_back(oss.str()); return c;}
std::string combine_cmd_line(const std::vector<std::string>& cmd) { std::ostringstream ret; for (unsigned int i = 0; i < cmd.size(); i++) { const auto& c = cmd[i]; ret << "\""; for (char i : c) { switch (i) { case '\a': ret << "\\a"; break; case '\b': ret << "\\b"; break; case '\f': ret << "\\f"; break; case '\n': ret << "\\n"; break; case '\r': ret << "\\r"; break; case '\t': ret << "\\t"; break; case '\v': ret << "\\v"; break; case '"': ret << "\\\""; break; case '\\': ret << "\\\\"; break; default: ret << i; break; } } ret << "\""; if (i < cmd.size() - 1) { ret << " "; } } return ret.str();}
void inflate_string(std::string& s) { auto* cc = reinterpret_cast<unsigned char*>(&s[0]); // autodetect compressed string if (s.size() >= 2 && ((cc[0] == 0x1F && cc[1] == 0x8B) // gzip || (cc[0] == 0x78 && (cc[1] == 0x01 // zlib || cc[1] == 0x9C || cc[1] == 0xDA)))) { const int BUF_SIZE = 1024; unsigned char s_outbuf[BUF_SIZE]; z_stream stream; std::memset(&stream, 0, sizeof(stream));
unsigned char* dataStart; int windowBits; size_t dataLen; if (cc[0] == 0x1F && cc[1] == 0x8B) { dataStart = cc + 10; windowBits = -Z_DEFAULT_WINDOW_BITS; if ((cc[3] & 0x4) != 0) { dataStart += 2; if (dataStart >= cc + s.size()) { throw(-1); } } if ((cc[3] & 0x8) != 0) { while (*dataStart != '\0') { dataStart++; if (dataStart >= cc + s.size()) { throw(-1); } } dataStart++; if (dataStart >= cc + s.size()) { throw(-1); } } if ((cc[3] & 0x10) != 0) { while (*dataStart != '\0') { dataStart++; if (dataStart >= cc + s.size()) { throw(-1); } } dataStart++; if (dataStart >= cc + s.size()) { throw(-1); } } if ((cc[3] & 0x2) != 0) { dataStart += 2; if (dataStart >= cc + s.size()) { throw(-1); } } dataLen = s.size() - (dataStart - cc); } else { dataStart = cc; windowBits = Z_DEFAULT_WINDOW_BITS; dataLen = s.size(); }
stream.next_in = dataStart; stream.avail_in = static_cast<unsigned int>(dataLen); stream.next_out = &s_outbuf[0]; stream.avail_out = BUF_SIZE; int status = inflateInit2(&stream, windowBits); if (status != Z_OK) { throw(status); } std::ostringstream oss; while (true) { status = inflate(&stream, Z_NO_FLUSH); if (status == Z_STREAM_END || (stream.avail_out == 0U)) { // output buffer full or compression finished oss << std::string(reinterpret_cast<char*>(s_outbuf), BUF_SIZE - stream.avail_out); stream.next_out = &s_outbuf[0]; stream.avail_out = BUF_SIZE; } if (status == Z_STREAM_END) { break; } if (status != Z_OK) { throw(status); } } status = inflateEnd(&stream); if (status != Z_OK) { throw(status); } s = oss.str(); }}
std::string deflate_string(const std::string& s) { mz_ulong compressedLength = compressBound(static_cast<mz_ulong>(s.size())); auto* cmpr = static_cast<unsigned char*>(::malloc(compressedLength * sizeof(unsigned char))); int status = compress(cmpr, &compressedLength, reinterpret_cast<const unsigned char*>(&s[0]), static_cast<mz_ulong>(s.size())); if (status != Z_OK) { ::free(cmpr); throw(status); } std::string ret(reinterpret_cast<const char*>(cmpr), compressedLength); ::free(cmpr); return ret;}
std::string encode_base64(const std::string& s) { base64::encoder E; std::ostringstream oss; oss << "@"; // add leading "@" to distinguish from valid MiniZinc code std::istringstream iss(s); E.encode(iss, oss); return oss.str();}
std::string decode_base64(const std::string& s) { if (s.empty() || s[0] != '@') { throw InternalError("string is not base64 encoded"); } base64::decoder D; std::ostringstream oss; std::istringstream iss(s); (void)iss.get(); // remove leading "@" D.decode(iss, oss); return oss.str();}
#ifdef _WIN32std::string wide_to_utf8(const wchar_t* str, int size) { int buffer_size = WideCharToMultiByte(CP_UTF8, 0, str, size, nullptr, 0, nullptr, nullptr); if (buffer_size == 0) { return ""; } std::string result(buffer_size - 1, '\0'); WideCharToMultiByte(CP_UTF8, 0, str, size, &result[0], buffer_size, nullptr, nullptr); return result;}
std::string wide_to_utf8(const std::wstring& str) { return wide_to_utf8(str.c_str(), -1); }
std::wstring utf8_to_wide(const std::string& str) { int buffer_size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0); if (buffer_size == 0) { return L""; } std::wstring result(buffer_size - 1, '\0'); MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &result[0], buffer_size); return result;}#endif} // namespace FileUtils} // namespace MiniZinc