this repo has no description
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