this repo has no description
at develop 18 kB view raw
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* 4 * Main authors: 5 * Guido Tack <guido.tack@monash.edu> 6 */ 7 8/* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12#ifdef _MSC_VER 13#define _CRT_SECURE_NO_WARNINGS 14#endif 15 16#include <minizinc/config.hh> 17#include <minizinc/exception.hh> 18#include <minizinc/file_utils.hh> 19#include <minizinc/thirdparty/b64/decode.h> 20#include <minizinc/thirdparty/b64/encode.h> 21#include <minizinc/thirdparty/miniz.h> 22 23#include <cstring> 24#include <sstream> 25#include <string> 26 27#ifdef HAS_PIDPATH 28#include <libproc.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32#include <unistd.h> 33#elif defined(HAS_GETMODULEFILENAME) || defined(HAS_GETFILEATTRIBUTES) 34#include <windows.h> 35#else 36#include <unistd.h> 37#endif 38#include <sys/stat.h> 39#include <sys/types.h> 40 41#ifdef _MSC_VER 42#include "Shlwapi.h" 43#pragma comment(lib, "Shlwapi.lib") 44#include "Shlobj.h" 45 46#include <direct.h> 47#else 48#include <dirent.h> 49#include <ftw.h> 50#include <libgen.h> 51#endif 52 53namespace MiniZinc { 54namespace FileUtils { 55 56#ifdef HAS_PIDPATH 57std::string progpath(void) { 58 pid_t pid = getpid(); 59 char path[PROC_PIDPATHINFO_MAXSIZE]; 60 int ret = proc_pidpath(pid, path, sizeof(path)); 61 if (ret <= 0) { 62 return ""; 63 } else { 64 std::string p(path); 65 size_t slash = p.find_last_of("/"); 66 if (slash != std::string::npos) { 67 p = p.substr(0, slash); 68 } 69 return p; 70 } 71} 72#elif defined(HAS_GETMODULEFILENAME) 73std::string progpath(void) { 74 char path[MAX_PATH]; 75 int ret = GetModuleFileName(NULL, path, MAX_PATH); 76 if (ret <= 0) { 77 return ""; 78 } else { 79 std::string p(path); 80 size_t slash = p.find_last_of("/\\"); 81 if (slash != std::string::npos) { 82 p = p.substr(0, slash); 83 } 84 return p; 85 } 86} 87#else 88std::string progpath(void) { 89 const int bufsz = 2000; 90 char path[bufsz + 1]; 91 ssize_t sz = readlink("/proc/self/exe", path, bufsz); 92 if (sz < 0) { 93 return ""; 94 } else { 95 path[sz] = '\0'; 96 std::string p(path); 97 size_t slash = p.find_last_of("/"); 98 if (slash != std::string::npos) { 99 p = p.substr(0, slash); 100 } 101 return p; 102 } 103} 104#endif 105 106bool file_exists(const std::string& filename) { 107#if defined(HAS_GETFILEATTRIBUTES) 108 DWORD dwAttrib = GetFileAttributes(filename.c_str()); 109 110 return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); 111#else 112 struct stat info; 113 return stat(filename.c_str(), &info) == 0 && (info.st_mode & S_IFREG); 114#endif 115} 116 117bool directory_exists(const std::string& dirname) { 118#if defined(HAS_GETFILEATTRIBUTES) 119 DWORD dwAttrib = GetFileAttributes(dirname.c_str()); 120 121 return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); 122#else 123 struct stat info; 124 return stat(dirname.c_str(), &info) == 0 && (info.st_mode & S_IFDIR); 125#endif 126} 127 128std::string file_path(const std::string& filename, const std::string& basePath) { 129#ifdef _MSC_VER 130 LPSTR lpBuffer, lpFilePart; 131 DWORD nBufferLength = GetFullPathName(filename.c_str(), 0, 0, &lpFilePart); 132 if (!(lpBuffer = (LPTSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * nBufferLength))) return 0; 133 std::string ret; 134 DWORD error = GetFullPathName(filename.c_str(), nBufferLength, lpBuffer, &lpFilePart); 135 DWORD fileAttr = GetFileAttributes(lpBuffer); 136 DWORD lastError = GetLastError(); 137 138 if (error == 0 || (fileAttr == INVALID_FILE_ATTRIBUTES && lastError != NO_ERROR)) { 139 if (basePath.empty()) 140 ret = filename; 141 else 142 ret = file_path(basePath + "/" + filename); 143 } else { 144 ret = std::string(lpBuffer); 145 } 146 LocalFree(lpBuffer); 147 return ret; 148#else 149 char* rp = realpath(filename.c_str(), NULL); 150 if (rp == NULL) { 151 if (basePath.empty()) 152 return filename; 153 else 154 return file_path(basePath + "/" + filename); 155 } 156 std::string rp_s(rp); 157 free(rp); 158 return rp_s; 159#endif 160} 161 162std::string dir_name(const std::string& filename) { 163#ifdef _MSC_VER 164 size_t pos = filename.find_last_of("\\/"); 165 return (pos == std::string::npos) ? "" : filename.substr(0, pos); 166#else 167 char* fn = strdup(filename.c_str()); 168 char* dn = dirname(fn); 169 std::string ret(dn); 170 free(fn); 171 return ret; 172#endif 173} 174 175std::string base_name(const std::string& filename) { 176#ifdef _MSC_VER 177 size_t pos = filename.find_last_of("\\/"); 178 return (pos == std::string::npos) ? filename : filename.substr(pos + 1); 179#else 180 char* fn = strdup(filename.c_str()); 181 char* dn = basename(fn); 182 std::string ret(dn); 183 free(fn); 184 return ret; 185#endif 186} 187 188bool is_absolute(const std::string& path) { 189#ifdef _MSC_VER 190 return !PathIsRelative(path.c_str()); 191#else 192 return path.empty() ? false : (path[0] == '/'); 193#endif 194} 195 196std::string find_executable(const std::string& filename) { 197 if (is_absolute(filename)) { 198 if (file_exists(filename)) { 199 return filename; 200 } 201#ifdef _MSC_VER 202 if (FileUtils::file_exists(filename + ".exe")) { 203 return filename + ".exe"; 204 } else if (FileUtils::file_exists(filename + ".bat")) { 205 return filename + ".bat"; 206 } 207#endif 208 return ""; 209 } 210 char* path_c = getenv("PATH"); 211#ifdef _MSC_VER 212 char pathsep = ';'; 213#else 214 char pathsep = ':'; 215#endif 216 std::string path; 217 if (path_c) { 218 path = path_c; 219 if (path.size()) { 220 path += pathsep; 221 } 222 } 223 path += progpath(); 224 std::string pathItem; 225 std::stringstream pathStream(path); 226 while (std::getline(pathStream, pathItem, pathsep)) { 227 std::string fileWithPath = pathItem + "/" + filename; 228 if (file_exists(fileWithPath)) return fileWithPath; 229#ifdef _MSC_VER 230 if (FileUtils::file_exists(fileWithPath + ".exe")) { 231 return fileWithPath + ".exe"; 232 } else if (FileUtils::file_exists(fileWithPath + ".bat")) { 233 return fileWithPath + ".bat"; 234 } 235#endif 236 } 237 return ""; 238} 239 240std::vector<std::string> directory_list(const std::string& dir, const std::string& ext) { 241 std::vector<std::string> entries; 242#ifdef _MSC_VER 243 WIN32_FIND_DATA findData; 244 HANDLE hFind = ::FindFirstFile((dir + "/*." + ext).c_str(), &findData); 245 if (hFind != INVALID_HANDLE_VALUE) { 246 do { 247 if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { 248 entries.push_back(findData.cFileName); 249 } 250 } while (::FindNextFile(hFind, &findData)); 251 ::FindClose(hFind); 252 } 253#else 254 DIR* dirp = opendir(dir.c_str()); 255 if (dirp) { 256 struct dirent* dp; 257 while ((dp = readdir(dirp)) != NULL) { 258 std::string fileName(dp->d_name); 259 struct stat info; 260 if (stat((dir + "/" + fileName).c_str(), &info) == 0 && (info.st_mode & S_IFREG)) { 261 if (ext == "*") { 262 entries.push_back(fileName); 263 } else { 264 if (fileName.size() > ext.size() + 2 && 265 fileName.substr(fileName.size() - ext.size() - 1) == "." + ext) { 266 entries.push_back(fileName); 267 } 268 } 269 } 270 } 271 closedir(dirp); 272 } 273#endif 274 return entries; 275} 276 277std::string working_directory(void) { 278 char wd[FILENAME_MAX]; 279#ifdef _MSC_VER 280 if (!_getcwd(wd, sizeof(wd))) return ""; 281#else 282 if (!getcwd(wd, sizeof(wd))) return ""; 283#endif 284 return wd; 285} 286 287std::string share_directory(void) { 288 if (char* MZNSTDLIBDIR = getenv("MZN_STDLIB_DIR")) { 289 return std::string(MZNSTDLIBDIR); 290 } 291 std::string mypath = FileUtils::progpath(); 292 int depth = 0; 293 for (unsigned int i = 0; i < mypath.size(); i++) 294 if (mypath[i] == '/' || mypath[i] == '\\') depth++; 295 for (int i = 0; i <= depth; i++) { 296 if (FileUtils::file_exists(mypath + "/share/minizinc/std/builtins.mzn")) 297 return mypath + "/share/minizinc"; 298 mypath += "/.."; 299 } 300 return ""; 301} 302 303std::string user_config_dir(void) { 304#ifdef _MSC_VER 305 HRESULT hr; 306 PWSTR pszPath = NULL; 307 308 hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &pszPath); 309 if (SUCCEEDED(hr)) { 310 int charsRequired = WideCharToMultiByte(CP_ACP, 0, pszPath, -1, 0, 0, 0, 0); 311 std::string configPath; 312 if (charsRequired > 0) { 313 char* tmp = new char[charsRequired]; 314 if (WideCharToMultiByte(CP_ACP, 0, pszPath, -1, tmp, charsRequired, 0, 0) != 0) { 315 tmp[charsRequired - 1] = 0; 316 configPath = tmp; 317 } 318 delete[] tmp; 319 } 320 CoTaskMemFree(pszPath); 321 if (configPath.empty()) { 322 return ""; 323 } else { 324 return configPath + "/MiniZinc"; 325 } 326 } 327 return ""; 328#else 329 if (const char* hd = getenv("HOME")) { 330 return std::string(hd) + "/.minizinc"; 331 } 332 return ""; 333#endif 334} 335 336std::string global_config_file(void) { 337 std::string sd = share_directory(); 338 if (sd.empty()) return ""; 339 return sd + "/Preferences.json"; 340} 341 342std::string user_config_file(void) { return user_config_dir() + "/Preferences.json"; } 343 344TmpFile::TmpFile(const std::string& ext) { 345#ifdef _WIN32 346 TCHAR szTempFileName[MAX_PATH]; 347 TCHAR lpTempPathBuffer[MAX_PATH]; 348 349 GetTempPath(MAX_PATH, lpTempPathBuffer); 350 GetTempFileName(lpTempPathBuffer, "tmp_mzn_", 0, szTempFileName); 351 352 _name = szTempFileName; 353 MoveFile(_name.c_str(), (_name + ext).c_str()); 354 _name += ext; 355#else 356 _tmpfile_desc = -1; 357 _name = "/tmp/mznfileXXXXXX" + ext; 358 char* tmpfile = strndup(_name.c_str(), _name.size()); 359 _tmpfile_desc = mkstemps(tmpfile, ext.size()); 360 if (_tmpfile_desc == -1) { 361 ::free(tmpfile); 362 throw InternalError("Error occurred when creating temporary file"); 363 } 364 _name = std::string(tmpfile); 365 ::free(tmpfile); 366#endif 367} 368 369TmpFile::~TmpFile(void) { 370 remove(_name.c_str()); 371#ifndef _WIN32 372 if (_tmpfile_desc != -1) close(_tmpfile_desc); 373#endif 374} 375 376TmpDir::TmpDir(void) { 377#ifdef _WIN32 378 TCHAR szTempFileName[MAX_PATH]; 379 TCHAR lpTempPathBuffer[MAX_PATH]; 380 381 GetTempPath(MAX_PATH, lpTempPathBuffer); 382 GetTempFileName(lpTempPathBuffer, "tmp_mzn_", 0, szTempFileName); 383 384 _name = szTempFileName; 385 DeleteFile(_name.c_str()); 386 CreateDirectory(_name.c_str(), NULL); 387#else 388 _name = "/tmp/mzndirXXXXXX"; 389 char* tmpfile = strndup(_name.c_str(), _name.size()); 390 391 if (mkdtemp(tmpfile) == NULL) { 392 ::free(tmpfile); 393 throw InternalError("Error occurred when creating temporary directory"); 394 } 395 _name = std::string(tmpfile); 396 ::free(tmpfile); 397#endif 398} 399 400#ifdef _WIN32 401namespace { 402void remove_dir(const std::string& d) { 403 HANDLE dh; 404 WIN32_FIND_DATA info; 405 406 std::string pattern = d + "\\*.*"; 407 dh = ::FindFirstFile(pattern.c_str(), &info); 408 if (dh != INVALID_HANDLE_VALUE) { 409 do { 410 if (info.cFileName[0] != '.') { 411 std::string fp = d + "\\" + info.cFileName; 412 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 413 remove_dir(fp); 414 } else { 415 ::SetFileAttributes(fp.c_str(), FILE_ATTRIBUTE_NORMAL); 416 ::DeleteFile(fp.c_str()); 417 } 418 } 419 } while (::FindNextFile(dh, &info) == TRUE); 420 } 421 ::FindClose(dh); 422 ::SetFileAttributes(d.c_str(), FILE_ATTRIBUTE_NORMAL); 423 ::RemoveDirectory(d.c_str()); 424} 425} // namespace 426#else 427namespace { 428int remove_file(const char* fpath, const struct stat*, int, struct FTW*) { return unlink(fpath); } 429} // namespace 430#endif 431 432TmpDir::~TmpDir(void) { 433#ifdef _WIN32 434 remove_dir(_name); 435#else 436 nftw(_name.c_str(), remove_file, 64, FTW_DEPTH | FTW_PHYS); 437 rmdir(_name.c_str()); 438#endif 439} 440 441std::vector<std::string> parseCmdLine(const std::string& s) { 442 // Break the string up at whitespace, except inside quotes, but ignore escaped quotes 443 std::vector<std::string> c; 444 size_t cur = 0; 445 size_t l = s.length(); 446 std::ostringstream oss; 447 bool inside_quote = false; 448 bool had_escape = false; 449 for (; cur < l; cur++) { 450 if (inside_quote) { 451 if (s[cur] == '"') { 452 if (had_escape) { 453 oss << "\""; 454 had_escape = false; 455 } else { 456 inside_quote = false; 457 } 458 } else if (s[cur] == '\\') { 459 had_escape = true; 460 } else { 461 if (had_escape) { 462 oss << "\\"; 463 had_escape = false; 464 } 465 oss << s[cur]; 466 } 467 } else { 468 if (s[cur] == ' ') { 469 if (had_escape) { 470 oss << " "; 471 had_escape = false; 472 } else { 473 c.push_back(oss.str()); 474 oss.str(std::string()); 475 } 476 } else if (s[cur] == '\\') { 477 if (had_escape) { 478 oss << "\\"; 479 had_escape = false; 480 } else { 481 had_escape = true; 482 } 483 } else if (s[cur] == '"') { 484 if (had_escape) { 485 oss << "\""; 486 had_escape = false; 487 } else { 488 inside_quote = true; 489 } 490 } else { 491 if (had_escape) { 492 switch (s[cur]) { 493 case 'a': 494 oss << "\a"; 495 break; 496 case 'b': 497 oss << "\b"; 498 break; 499 case 'f': 500 oss << "\f"; 501 break; 502 case 'n': 503 oss << "\n"; 504 break; 505 case 'r': 506 oss << "\r"; 507 break; 508 case 't': 509 oss << "\t"; 510 break; 511 case 'v': 512 oss << "\v"; 513 break; 514 default: 515 oss << "\\" << s[cur]; 516 break; 517 } 518 had_escape = false; 519 } else { 520 oss << s[cur]; 521 } 522 } 523 } 524 } 525 c.push_back(oss.str()); 526 return c; 527} 528 529std::string combineCmdLine(const std::vector<std::string>& cmd) { 530 std::ostringstream ret; 531 for (unsigned int i = 0; i < cmd.size(); i++) { 532 auto& c = cmd[i]; 533 ret << "\""; 534 for (size_t i = 0; i < c.size(); i++) { 535 switch (c[i]) { 536 case '\a': 537 ret << "\\a"; 538 break; 539 case '\b': 540 ret << "\\b"; 541 break; 542 case '\f': 543 ret << "\\f"; 544 break; 545 case '\n': 546 ret << "\\n"; 547 break; 548 case '\r': 549 ret << "\\r"; 550 break; 551 case '\t': 552 ret << "\\t"; 553 break; 554 case '\v': 555 ret << "\\v"; 556 break; 557 case '"': 558 ret << "\\\""; 559 break; 560 case '\\': 561 ret << "\\\\"; 562 break; 563 default: 564 ret << c[i]; 565 break; 566 } 567 } 568 ret << "\""; 569 if (i < cmd.size() - 1) { 570 ret << " "; 571 } 572 } 573 return ret.str(); 574} 575 576void inflateString(std::string& s) { 577 unsigned char* cc = reinterpret_cast<unsigned char*>(&s[0]); 578 // autodetect compressed string 579 if (s.size() >= 2 && ((cc[0] == 0x1F && cc[1] == 0x8B) // gzip 580 || (cc[0] == 0x78 && (cc[1] == 0x01 // zlib 581 || cc[1] == 0x9C || cc[1] == 0xDA)))) { 582 const int BUF_SIZE = 1024; 583 unsigned char s_outbuf[BUF_SIZE]; 584 z_stream stream; 585 std::memset(&stream, 0, sizeof(stream)); 586 587 unsigned char* dataStart; 588 int windowBits; 589 size_t dataLen; 590 if (cc[0] == 0x1F && cc[1] == 0x8B) { 591 dataStart = cc + 10; 592 windowBits = -Z_DEFAULT_WINDOW_BITS; 593 if (cc[3] & 0x4) { 594 dataStart += 2; 595 if (dataStart >= cc + s.size()) throw(-1); 596 } 597 if (cc[3] & 0x8) { 598 while (*dataStart != '\0') { 599 dataStart++; 600 if (dataStart >= cc + s.size()) throw(-1); 601 } 602 dataStart++; 603 if (dataStart >= cc + s.size()) throw(-1); 604 } 605 if (cc[3] & 0x10) { 606 while (*dataStart != '\0') { 607 dataStart++; 608 if (dataStart >= cc + s.size()) throw(-1); 609 } 610 dataStart++; 611 if (dataStart >= cc + s.size()) throw(-1); 612 } 613 if (cc[3] & 0x2) { 614 dataStart += 2; 615 if (dataStart >= cc + s.size()) throw(-1); 616 } 617 dataLen = s.size() - (dataStart - cc); 618 } else { 619 dataStart = cc; 620 windowBits = Z_DEFAULT_WINDOW_BITS; 621 dataLen = s.size(); 622 } 623 624 stream.next_in = dataStart; 625 stream.avail_in = static_cast<unsigned int>(dataLen); 626 stream.next_out = &s_outbuf[0]; 627 stream.avail_out = BUF_SIZE; 628 int status = inflateInit2(&stream, windowBits); 629 if (status != Z_OK) throw(status); 630 std::ostringstream oss; 631 while (true) { 632 status = inflate(&stream, Z_NO_FLUSH); 633 if (status == Z_STREAM_END || !stream.avail_out) { 634 // output buffer full or compression finished 635 oss << std::string(reinterpret_cast<char*>(s_outbuf), BUF_SIZE - stream.avail_out); 636 stream.next_out = &s_outbuf[0]; 637 stream.avail_out = BUF_SIZE; 638 } 639 if (status == Z_STREAM_END) break; 640 if (status != Z_OK) throw(status); 641 } 642 status = inflateEnd(&stream); 643 if (status != Z_OK) throw(status); 644 s = oss.str(); 645 } 646} 647 648std::string deflateString(const std::string& s) { 649 mz_ulong compressedLength = compressBound(static_cast<mz_ulong>(s.size())); 650 unsigned char* cmpr = 651 static_cast<unsigned char*>(::malloc(compressedLength * sizeof(unsigned char))); 652 int status = compress(cmpr, &compressedLength, reinterpret_cast<const unsigned char*>(&s[0]), 653 static_cast<mz_ulong>(s.size())); 654 if (status != Z_OK) { 655 ::free(cmpr); 656 throw(status); 657 } 658 std::string ret(reinterpret_cast<const char*>(cmpr), compressedLength); 659 ::free(cmpr); 660 return ret; 661} 662 663std::string encodeBase64(const std::string& s) { 664 base64::encoder E; 665 std::ostringstream oss; 666 oss << "@"; // add leading "@" to distinguish from valid MiniZinc code 667 std::istringstream iss(s); 668 E.encode(iss, oss); 669 return oss.str(); 670} 671 672std::string decodeBase64(const std::string& s) { 673 if (s.size() == 0 || s[0] != '@') throw InternalError("string is not base64 encoded"); 674 base64::decoder D; 675 std::ostringstream oss; 676 std::istringstream iss(s); 677 (void)iss.get(); // remove leading "@" 678 D.decode(iss, oss); 679 return oss.str(); 680} 681 682} // namespace FileUtils 683} // namespace MiniZinc