this repo has no description
at develop 20 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/_thirdparty/b64/decode.h> 17#include <minizinc/_thirdparty/b64/encode.h> 18#include <minizinc/_thirdparty/miniz.h> 19#include <minizinc/config.hh> 20#include <minizinc/exception.hh> 21#include <minizinc/file_utils.hh> 22 23#include <cstring> 24#include <sstream> 25#include <string> 26 27#ifdef HAS_PIDPATH 28#include <cstdio> 29#include <cstdlib> 30#include <cstring> 31#include <libproc.h> 32#include <unistd.h> 33#elif defined(HAS_GETMODULEFILENAME) || defined(HAS_GETFILEATTRIBUTES) 34#define NOMINMAX // Ensure the words min/max remain available 35#include <windows.h> 36#undef ERROR 37#else 38#include <unistd.h> 39#endif 40#include <sys/stat.h> 41#include <sys/types.h> 42 43#ifdef _MSC_VER 44#include "Shlwapi.h" 45#pragma comment(lib, "Shlwapi.lib") 46#include "Shlobj.h" 47 48#include <direct.h> 49#else 50#include <dirent.h> 51#include <ftw.h> 52#include <libgen.h> 53#endif 54 55namespace MiniZinc { 56namespace FileUtils { 57 58#ifdef HAS_PIDPATH 59std::string progpath() { 60 pid_t pid = getpid(); 61 char path[PROC_PIDPATHINFO_MAXSIZE]; 62 int ret = proc_pidpath(pid, path, sizeof(path)); 63 if (ret <= 0) { 64 return ""; 65 } 66 std::string p(path); 67 size_t slash = p.find_last_of('/'); 68 if (slash != std::string::npos) { 69 p = p.substr(0, slash); 70 } 71 return p; 72} 73#elif defined(HAS_GETMODULEFILENAME) 74std::string progpath() { 75 wchar_t path[MAX_PATH]; 76 int ret = GetModuleFileNameW(nullptr, path, MAX_PATH); 77 if (ret <= 0) { 78 return ""; 79 } 80 std::string p = wide_to_utf8(path); 81 size_t slash = p.find_last_of("/\\"); 82 if (slash != std::string::npos) { 83 p = p.substr(0, slash); 84 } 85 return p; 86} 87#else 88std::string progpath() { 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 } 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#endif 104 105bool file_exists(const std::string& filename) { 106#if defined(HAS_GETFILEATTRIBUTES) 107 DWORD dwAttrib = GetFileAttributesW(utf8_to_wide(filename).c_str()); 108 109 return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0; 110#else 111 struct stat info; 112 return stat(filename.c_str(), &info) == 0 && ((info.st_mode & S_IFREG) != 0); 113#endif 114} 115 116bool directory_exists(const std::string& dirname) { 117#if defined(HAS_GETFILEATTRIBUTES) 118 DWORD dwAttrib = GetFileAttributesW(utf8_to_wide(dirname).c_str()); 119 120 return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0; 121#else 122 struct stat info; 123 return stat(dirname.c_str(), &info) == 0 && ((info.st_mode & S_IFDIR) != 0); 124#endif 125} 126 127std::string file_path(const std::string& filename, const std::string& basePath) { 128#ifdef _MSC_VER 129 LPWSTR lpFilePart; 130 DWORD nBufferLength = GetFullPathNameW(utf8_to_wide(filename).c_str(), 0, nullptr, &lpFilePart); 131 auto lpBuffer = static_cast<LPWSTR>(LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * nBufferLength)); 132 if (lpBuffer == nullptr) { 133 return ""; 134 } 135 136 std::string ret; 137 DWORD error = 138 GetFullPathNameW(utf8_to_wide(filename).c_str(), nBufferLength, lpBuffer, &lpFilePart); 139 DWORD fileAttr = GetFileAttributesW(lpBuffer); 140 DWORD lastError = GetLastError(); 141 142 if (error == 0 || (fileAttr == INVALID_FILE_ATTRIBUTES && lastError != NO_ERROR)) { 143 ret = basePath.empty() ? filename : file_path(basePath + "/" + filename); 144 } else { 145 ret = wide_to_utf8(lpBuffer); 146 } 147 LocalFree(lpBuffer); 148 return ret; 149#else 150 char* rp = realpath(filename.c_str(), nullptr); 151 if (rp == nullptr) { 152 if (basePath.empty()) { 153 return filename; 154 } 155 return file_path(basePath + "/" + filename); 156 } 157 std::string rp_s(rp); 158 free(rp); 159 return rp_s; 160#endif 161} 162 163std::string dir_name(const std::string& filename) { 164#ifdef _MSC_VER 165 size_t pos = filename.find_last_of("\\/"); 166 return (pos == std::string::npos) ? "" : filename.substr(0, pos); 167#else 168 char* fn = strdup(filename.c_str()); 169 char* dn = dirname(fn); 170 std::string ret(dn); 171 free(fn); 172 return ret; 173#endif 174} 175 176std::string base_name(const std::string& filename) { 177#ifdef _MSC_VER 178 size_t pos = filename.find_last_of("\\/"); 179 return (pos == std::string::npos) ? filename : filename.substr(pos + 1); 180#else 181 char* fn = strdup(filename.c_str()); 182 char* dn = basename(fn); 183 std::string ret(dn); 184 free(fn); 185 return ret; 186#endif 187} 188 189bool is_absolute(const std::string& path) { 190#ifdef _MSC_VER 191 if (path.size() > 2 && 192 ((path[0] == '\\' && path[1] == '\\') || (path[0] == '/' && path[1] == '/'))) { 193 return true; 194 } 195 return PathIsRelativeW(utf8_to_wide(path).c_str()) == FALSE; 196#else 197 return path.empty() ? false : (path[0] == '/'); 198#endif 199} 200 201std::string find_executable(const std::string& filename) { 202 if (is_absolute(filename)) { 203 if (file_exists(filename)) { 204 return filename; 205 } 206#ifdef _MSC_VER 207 if (FileUtils::file_exists(filename + ".exe")) { 208 return filename + ".exe"; 209 } 210 if (FileUtils::file_exists(filename + ".bat")) { 211 return filename + ".bat"; 212 } 213#endif 214 return ""; 215 } 216 char* path_c = getenv("PATH"); 217#ifdef _MSC_VER 218 char pathsep = ';'; 219#else 220 char pathsep = ':'; 221#endif 222 std::string path; 223 if (path_c != nullptr) { 224 path = path_c; 225 if (!path.empty()) { 226 path += pathsep; 227 } 228 } 229 path += progpath(); 230 std::string pathItem; 231 std::stringstream pathStream(path); 232 while (std::getline(pathStream, pathItem, pathsep)) { 233 std::string fileWithPath = pathItem.append("/").append(filename); 234 if (file_exists(fileWithPath)) { 235 return fileWithPath; 236 } 237#ifdef _MSC_VER 238 if (FileUtils::file_exists(fileWithPath + ".exe")) { 239 return fileWithPath + ".exe"; 240 } 241 if (FileUtils::file_exists(fileWithPath + ".bat")) { 242 return fileWithPath + ".bat"; 243 } 244#endif 245 } 246 return ""; 247} 248 249std::vector<std::string> directory_list(const std::string& dir, const std::string& ext) { 250 std::vector<std::string> entries; 251#ifdef _MSC_VER 252 WIN32_FIND_DATAW findData; 253 HANDLE hFind = ::FindFirstFileW(utf8_to_wide(dir + "/*." + ext).c_str(), &findData); 254 if (hFind != INVALID_HANDLE_VALUE) { 255 do { 256 if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { 257 entries.push_back(wide_to_utf8(findData.cFileName)); 258 } 259 } while (::FindNextFileW(hFind, &findData) == TRUE); 260 ::FindClose(hFind); 261 } 262#else 263 DIR* dirp = opendir(dir.c_str()); 264 if (dirp != nullptr) { 265 struct dirent* dp; 266 while ((dp = readdir(dirp)) != nullptr) { 267 std::string fileName(dp->d_name); 268 struct stat info; 269 if (stat(((dir + "/").append(fileName)).c_str(), &info) == 0 && 270 ((info.st_mode & S_IFREG) != 0)) { 271 if (ext == "*") { 272 entries.push_back(fileName); 273 } else { 274 if (fileName.size() > ext.size() + 2 && 275 fileName.substr(fileName.size() - ext.size() - 1) == "." + ext) { 276 entries.push_back(fileName); 277 } 278 } 279 } 280 } 281 closedir(dirp); 282 } 283#endif 284 return entries; 285} 286 287std::string working_directory() { 288#ifdef _MSC_VER 289 wchar_t wd[FILENAME_MAX]; 290 if (_wgetcwd(wd, FILENAME_MAX) == FALSE) { 291 return ""; 292 } 293 return wide_to_utf8(wd); 294#else 295 char wd[FILENAME_MAX]; 296 if (getcwd(wd, sizeof(wd)) == nullptr) { 297 return ""; 298 } 299 return wd; 300#endif 301} 302 303std::string share_directory() { 304#ifdef _WIN32 305 if (wchar_t* MZNSTDLIBDIR = _wgetenv(L"MZN_STDLIB_DIR")) { 306 return wide_to_utf8(MZNSTDLIBDIR); 307 } 308#else 309 if (char* MZNSTDLIBDIR = getenv("MZN_STDLIB_DIR")) { 310 return std::string(MZNSTDLIBDIR); 311 } 312#endif 313 // NOLINTNEXTLINE(readability-redundant-string-init) 314 std::string static_stdlib_dir(MZN_STATIC_STDLIB_DIR); 315 if (FileUtils::file_exists(static_stdlib_dir + "/std/stdlib.mzn")) { 316 return static_stdlib_dir; 317 } 318 std::string mypath = FileUtils::progpath(); 319 int depth = 0; 320 for (char i : mypath) { 321 if (i == '/' || i == '\\') { 322 depth++; 323 } 324 } 325 for (int i = 0; i <= depth; i++) { 326 if (FileUtils::file_exists(mypath + "/share/minizinc/std/stdlib.mzn")) { 327 return mypath + "/share/minizinc"; 328 } 329 mypath += "/.."; 330 } 331 return ""; 332} 333 334std::string user_config_dir() { 335#ifdef _MSC_VER 336 HRESULT hr; 337 PWSTR pszPath = nullptr; 338 339 hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pszPath); 340 if (SUCCEEDED(hr)) { 341 auto configPath = wide_to_utf8(pszPath); 342 CoTaskMemFree(pszPath); 343 if (configPath.empty()) { 344 return ""; 345 } 346 return configPath + "/MiniZinc"; 347 } 348 return ""; 349#else 350 if (const char* hd = getenv("HOME")) { 351 return std::string(hd) + "/.minizinc"; 352 } 353 return ""; 354#endif 355} 356 357std::string global_config_file() { 358 std::string sd = share_directory(); 359 if (sd.empty()) { 360 return ""; 361 } 362 return sd + "/Preferences.json"; 363} 364 365std::string user_config_file() { return user_config_dir() + "/Preferences.json"; } 366 367TmpFile::TmpFile(const std::string& ext) { 368#ifdef _WIN32 369 WCHAR szTempFileName[MAX_PATH]; 370 WCHAR lpTempPathBuffer[MAX_PATH]; 371 372 bool didCopy; 373 do { 374 GetTempPathW(MAX_PATH, lpTempPathBuffer); 375 GetTempFileNameW(lpTempPathBuffer, L"tmp_mzn_", 0, szTempFileName); 376 377 _name = wide_to_utf8(szTempFileName); 378 _tmpNames.push_back(_name); 379 didCopy = CopyFileW(szTempFileName, utf8_to_wide(_name + ext).c_str(), TRUE) == TRUE; 380 } while (!didCopy); 381 _name += ext; 382#else 383 _tmpfileDesc = -1; 384 _name = "/tmp/mznfileXXXXXX" + ext; 385 char* tmpfile = strndup(_name.c_str(), _name.size()); 386 _tmpfileDesc = mkstemps(tmpfile, ext.size()); 387 if (_tmpfileDesc == -1) { 388 ::free(tmpfile); 389 throw InternalError("Error occurred when creating temporary file"); 390 } 391 _name = std::string(tmpfile); 392 ::free(tmpfile); 393#endif 394} 395 396TmpFile::~TmpFile() { 397#ifdef _WIN32 398 _wremove(utf8_to_wide(_name).c_str()); // TODO: Is this necessary? 399 for (auto& n : _tmpNames) { 400 _wremove(utf8_to_wide(n).c_str()); 401 } 402#else 403 remove(_name.c_str()); 404 if (_tmpfileDesc != -1) { 405 close(_tmpfileDesc); 406 } 407#endif 408} 409 410TmpDir::TmpDir() { 411#ifdef _WIN32 412 WCHAR szTempFileName[MAX_PATH]; 413 WCHAR lpTempPathBuffer[MAX_PATH]; 414 415 GetTempPathW(MAX_PATH, lpTempPathBuffer); 416 GetTempFileNameW(lpTempPathBuffer, L"tmp_mzn_", 0, szTempFileName); 417 418 _name = wide_to_utf8(szTempFileName); 419 DeleteFileW(szTempFileName); 420 CreateDirectoryW(szTempFileName, nullptr); 421#else 422 _name = "/tmp/mzndirXXXXXX"; 423 char* tmpfile = strndup(_name.c_str(), _name.size()); 424 425 if (mkdtemp(tmpfile) == nullptr) { 426 ::free(tmpfile); 427 throw InternalError("Error occurred when creating temporary directory"); 428 } 429 _name = std::string(tmpfile); 430 ::free(tmpfile); 431#endif 432} 433 434#ifdef _WIN32 435namespace { 436void remove_dir(const std::string& d) { 437 HANDLE dh; 438 WIN32_FIND_DATAW info; 439 auto dw = utf8_to_wide(d); 440 auto pattern = dw + L"\\*.*"; 441 dh = ::FindFirstFileW(pattern.c_str(), &info); 442 if (dh != INVALID_HANDLE_VALUE) { 443 do { 444 if (info.cFileName[0] != L'.') { 445 auto fp = dw + L"\\" + info.cFileName; 446 if ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { 447 remove_dir(wide_to_utf8(fp)); 448 } else { 449 ::SetFileAttributesW(fp.c_str(), FILE_ATTRIBUTE_NORMAL); 450 ::DeleteFileW(fp.c_str()); 451 } 452 } 453 } while (::FindNextFileW(dh, &info) == TRUE); 454 } 455 ::FindClose(dh); 456 ::SetFileAttributesW(dw.c_str(), FILE_ATTRIBUTE_NORMAL); 457 ::RemoveDirectoryW(dw.c_str()); 458} 459} // namespace 460#else 461namespace { 462int remove_file(const char* fpath, const struct stat* /*s*/, int /*i*/, struct FTW* /*ftw*/) { 463 return unlink(fpath); 464} 465} // namespace 466#endif 467 468TmpDir::~TmpDir() { 469#ifdef _WIN32 470 remove_dir(_name); 471#else 472 nftw(_name.c_str(), remove_file, 64, FTW_DEPTH | FTW_PHYS); 473 rmdir(_name.c_str()); 474#endif 475} 476 477std::vector<std::string> parse_cmd_line(const std::string& s) { 478 // Break the string up at whitespace, except inside quotes, but ignore escaped quotes 479 std::vector<std::string> c; 480 size_t cur = 0; 481 size_t l = s.length(); 482 std::ostringstream oss; 483 bool inside_quote = false; 484 bool had_escape = false; 485 for (; cur < l; cur++) { 486 if (inside_quote) { 487 if (s[cur] == '"') { 488 if (had_escape) { 489 oss << "\""; 490 had_escape = false; 491 } else { 492 inside_quote = false; 493 } 494 } else if (s[cur] == '\\') { 495 had_escape = true; 496 } else { 497 if (had_escape) { 498 oss << "\\"; 499 had_escape = false; 500 } 501 oss << s[cur]; 502 } 503 } else { 504 if (s[cur] == ' ') { 505 if (had_escape) { 506 oss << " "; 507 had_escape = false; 508 } else { 509 c.push_back(oss.str()); 510 oss.str(std::string()); 511 } 512 } else if (s[cur] == '\\') { 513 if (had_escape) { 514 oss << "\\"; 515 had_escape = false; 516 } else { 517 had_escape = true; 518 } 519 } else if (s[cur] == '"') { 520 if (had_escape) { 521 oss << "\""; 522 had_escape = false; 523 } else { 524 inside_quote = true; 525 } 526 } else { 527 if (had_escape) { 528 switch (s[cur]) { 529 case 'a': 530 oss << "\a"; 531 break; 532 case 'b': 533 oss << "\b"; 534 break; 535 case 'f': 536 oss << "\f"; 537 break; 538 case 'n': 539 oss << "\n"; 540 break; 541 case 'r': 542 oss << "\r"; 543 break; 544 case 't': 545 oss << "\t"; 546 break; 547 case 'v': 548 oss << "\v"; 549 break; 550 default: 551 oss << "\\" << s[cur]; 552 break; 553 } 554 had_escape = false; 555 } else { 556 oss << s[cur]; 557 } 558 } 559 } 560 } 561 c.push_back(oss.str()); 562 return c; 563} 564 565std::string combine_cmd_line(const std::vector<std::string>& cmd) { 566 std::ostringstream ret; 567 for (unsigned int i = 0; i < cmd.size(); i++) { 568 const auto& c = cmd[i]; 569 ret << "\""; 570 for (char i : c) { 571 switch (i) { 572 case '\a': 573 ret << "\\a"; 574 break; 575 case '\b': 576 ret << "\\b"; 577 break; 578 case '\f': 579 ret << "\\f"; 580 break; 581 case '\n': 582 ret << "\\n"; 583 break; 584 case '\r': 585 ret << "\\r"; 586 break; 587 case '\t': 588 ret << "\\t"; 589 break; 590 case '\v': 591 ret << "\\v"; 592 break; 593 case '"': 594 ret << "\\\""; 595 break; 596 case '\\': 597 ret << "\\\\"; 598 break; 599 default: 600 ret << i; 601 break; 602 } 603 } 604 ret << "\""; 605 if (i < cmd.size() - 1) { 606 ret << " "; 607 } 608 } 609 return ret.str(); 610} 611 612void inflate_string(std::string& s) { 613 auto* cc = reinterpret_cast<unsigned char*>(&s[0]); 614 // autodetect compressed string 615 if (s.size() >= 2 && ((cc[0] == 0x1F && cc[1] == 0x8B) // gzip 616 || (cc[0] == 0x78 && (cc[1] == 0x01 // zlib 617 || cc[1] == 0x9C || cc[1] == 0xDA)))) { 618 const int BUF_SIZE = 1024; 619 unsigned char s_outbuf[BUF_SIZE]; 620 z_stream stream; 621 std::memset(&stream, 0, sizeof(stream)); 622 623 unsigned char* dataStart; 624 int windowBits; 625 size_t dataLen; 626 if (cc[0] == 0x1F && cc[1] == 0x8B) { 627 dataStart = cc + 10; 628 windowBits = -Z_DEFAULT_WINDOW_BITS; 629 if ((cc[3] & 0x4) != 0) { 630 dataStart += 2; 631 if (dataStart >= cc + s.size()) { 632 throw(-1); 633 } 634 } 635 if ((cc[3] & 0x8) != 0) { 636 while (*dataStart != '\0') { 637 dataStart++; 638 if (dataStart >= cc + s.size()) { 639 throw(-1); 640 } 641 } 642 dataStart++; 643 if (dataStart >= cc + s.size()) { 644 throw(-1); 645 } 646 } 647 if ((cc[3] & 0x10) != 0) { 648 while (*dataStart != '\0') { 649 dataStart++; 650 if (dataStart >= cc + s.size()) { 651 throw(-1); 652 } 653 } 654 dataStart++; 655 if (dataStart >= cc + s.size()) { 656 throw(-1); 657 } 658 } 659 if ((cc[3] & 0x2) != 0) { 660 dataStart += 2; 661 if (dataStart >= cc + s.size()) { 662 throw(-1); 663 } 664 } 665 dataLen = s.size() - (dataStart - cc); 666 } else { 667 dataStart = cc; 668 windowBits = Z_DEFAULT_WINDOW_BITS; 669 dataLen = s.size(); 670 } 671 672 stream.next_in = dataStart; 673 stream.avail_in = static_cast<unsigned int>(dataLen); 674 stream.next_out = &s_outbuf[0]; 675 stream.avail_out = BUF_SIZE; 676 int status = inflateInit2(&stream, windowBits); 677 if (status != Z_OK) { 678 throw(status); 679 } 680 std::ostringstream oss; 681 while (true) { 682 status = inflate(&stream, Z_NO_FLUSH); 683 if (status == Z_STREAM_END || (stream.avail_out == 0U)) { 684 // output buffer full or compression finished 685 oss << std::string(reinterpret_cast<char*>(s_outbuf), BUF_SIZE - stream.avail_out); 686 stream.next_out = &s_outbuf[0]; 687 stream.avail_out = BUF_SIZE; 688 } 689 if (status == Z_STREAM_END) { 690 break; 691 } 692 if (status != Z_OK) { 693 throw(status); 694 } 695 } 696 status = inflateEnd(&stream); 697 if (status != Z_OK) { 698 throw(status); 699 } 700 s = oss.str(); 701 } 702} 703 704std::string deflate_string(const std::string& s) { 705 mz_ulong compressedLength = compressBound(static_cast<mz_ulong>(s.size())); 706 auto* cmpr = static_cast<unsigned char*>(::malloc(compressedLength * sizeof(unsigned char))); 707 int status = compress(cmpr, &compressedLength, reinterpret_cast<const unsigned char*>(&s[0]), 708 static_cast<mz_ulong>(s.size())); 709 if (status != Z_OK) { 710 ::free(cmpr); 711 throw(status); 712 } 713 std::string ret(reinterpret_cast<const char*>(cmpr), compressedLength); 714 ::free(cmpr); 715 return ret; 716} 717 718std::string encode_base64(const std::string& s) { 719 base64::encoder E; 720 std::ostringstream oss; 721 oss << "@"; // add leading "@" to distinguish from valid MiniZinc code 722 std::istringstream iss(s); 723 E.encode(iss, oss); 724 return oss.str(); 725} 726 727std::string decode_base64(const std::string& s) { 728 if (s.empty() || s[0] != '@') { 729 throw InternalError("string is not base64 encoded"); 730 } 731 base64::decoder D; 732 std::ostringstream oss; 733 std::istringstream iss(s); 734 (void)iss.get(); // remove leading "@" 735 D.decode(iss, oss); 736 return oss.str(); 737} 738 739#ifdef _WIN32 740std::string wide_to_utf8(const wchar_t* str, int size) { 741 int buffer_size = WideCharToMultiByte(CP_UTF8, 0, str, size, nullptr, 0, nullptr, nullptr); 742 if (buffer_size == 0) { 743 return ""; 744 } 745 std::string result(buffer_size - 1, '\0'); 746 WideCharToMultiByte(CP_UTF8, 0, str, size, &result[0], buffer_size, nullptr, nullptr); 747 return result; 748} 749 750std::string wide_to_utf8(const std::wstring& str) { return wide_to_utf8(str.c_str(), -1); } 751 752std::wstring utf8_to_wide(const std::string& str) { 753 int buffer_size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0); 754 if (buffer_size == 0) { 755 return L""; 756 } 757 std::wstring result(buffer_size - 1, '\0'); 758 MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &result[0], buffer_size); 759 return result; 760} 761#endif 762} // namespace FileUtils 763} // namespace MiniZinc