| // |
| // Copyright (c) 2017 The Khronos Group Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| #include "os_helpers.h" |
| #include "errorHelpers.h" |
| |
| // ================================================================================================= |
| // C++ interface. |
| // ================================================================================================= |
| |
| #include <cerrno> // errno, error constants |
| #include <climits> // PATH_MAX |
| #include <cstdlib> // abort, _splitpath, _makepath |
| #include <cstring> // strdup, strerror_r |
| #include <sstream> |
| |
| #include <vector> |
| |
| #if defined(__ANDROID__) |
| #include <android/api-level.h> |
| #include "harness/mt19937.h" |
| #endif |
| |
| #if !defined(_WIN32) |
| #if defined(__APPLE__) |
| #include <sys/sysctl.h> |
| #endif |
| #include <unistd.h> |
| #endif |
| |
| |
| #define CHECK_PTR(ptr) \ |
| if ((ptr) == NULL) \ |
| { \ |
| abort(); \ |
| } |
| |
| typedef std::vector<char> buffer_t; |
| |
| #if !defined(PATH_MAX) |
| #define PATH_MAX 1000 |
| #endif |
| |
| int const _size = PATH_MAX + 1; // Initial buffer size for path. |
| int const _count = 8; // How many times we will try to double buffer size. |
| |
| // ------------------------------------------------------------------------------------------------- |
| // MacOS X |
| // ------------------------------------------------------------------------------------------------- |
| |
| #if defined(__APPLE__) |
| |
| |
| #include <mach-o/dyld.h> // _NSGetExecutablePath |
| #include <libgen.h> // dirname |
| |
| |
| static std::string |
| _err_msg(int err, // Error number (e. g. errno). |
| int level // Nesting level, for avoiding infinite recursion. |
| ) |
| { |
| |
| /* |
| There are 3 incompatible versions of strerror_r: |
| |
| char * strerror_r( int, char *, size_t ); // GNU version |
| int strerror_r( int, char *, size_t ); // BSD version |
| int strerror_r( int, char *, size_t ); // XSI version |
| |
| BSD version returns error code, while XSI version returns 0 or -1 and |
| sets errno. |
| |
| */ |
| |
| // BSD version of strerror_r. |
| buffer_t buffer(100); |
| int count = _count; |
| for (;;) |
| { |
| int rc = strerror_r(err, &buffer.front(), buffer.size()); |
| if (rc == EINVAL) |
| { |
| // Error code is not recognized, but anyway we got the message. |
| return &buffer.front(); |
| } |
| else if (rc == ERANGE) |
| { |
| // Buffer is not enough. |
| if (count > 0) |
| { |
| // Enlarge the buffer. |
| --count; |
| buffer.resize(buffer.size() * 2); |
| } |
| else |
| { |
| std::stringstream ostr; |
| ostr << "Error " << err << " " |
| << "(Getting error message failed: " |
| << "Buffer of " << buffer.size() |
| << " bytes is still too small" |
| << ")"; |
| return ostr.str(); |
| }; // if |
| } |
| else if (rc == 0) |
| { |
| // We got the message. |
| return &buffer.front(); |
| } |
| else |
| { |
| std::stringstream ostr; |
| ostr << "Error " << err << " " |
| << "(Getting error message failed: " |
| << (level < 2 ? _err_msg(rc, level + 1) : "Oops") << ")"; |
| return ostr.str(); |
| }; // if |
| }; // forever |
| |
| } // _err_msg |
| |
| |
| std::string dir_sep() { return "/"; } // dir_sep |
| |
| |
| std::string exe_path() |
| { |
| buffer_t path(_size); |
| int count = _count; |
| for (;;) |
| { |
| uint32_t size = path.size(); |
| int rc = _NSGetExecutablePath(&path.front(), &size); |
| if (rc == 0) |
| { |
| break; |
| }; // if |
| if (count > 0) |
| { |
| --count; |
| path.resize(size); |
| } |
| else |
| { |
| log_error("ERROR: Getting executable path failed: " |
| "_NSGetExecutablePath failed: Buffer of %lu bytes is " |
| "still too small\n", |
| (unsigned long)path.size()); |
| exit(2); |
| }; // if |
| }; // forever |
| return &path.front(); |
| } // exe_path |
| |
| |
| std::string exe_dir() |
| { |
| std::string path = exe_path(); |
| // We cannot pass path.c_str() to `dirname' bacause `dirname' modifies its |
| // argument. |
| buffer_t buffer(path.c_str(), |
| path.c_str() + path.size() + 1); // Copy with trailing zero. |
| return dirname(&buffer.front()); |
| } // exe_dir |
| |
| |
| #endif // __APPLE__ |
| |
| // ------------------------------------------------------------------------------------------------- |
| // Linux |
| // ------------------------------------------------------------------------------------------------- |
| |
| #if defined(__linux__) |
| |
| |
| #include <cerrno> // errno |
| #include <libgen.h> // dirname |
| #include <unistd.h> // readlink |
| |
| |
| static std::string _err_msg(int err, int level) |
| { |
| |
| /* |
| There are 3 incompatible versions of strerror_r: |
| |
| char * strerror_r( int, char *, size_t ); // GNU version |
| int strerror_r( int, char *, size_t ); // BSD version |
| int strerror_r( int, char *, size_t ); // XSI version |
| |
| BSD version returns error code, while XSI version returns 0 or -1 and |
| sets errno. |
| |
| */ |
| |
| #if (defined(__ANDROID__) && __ANDROID_API__ < 23) \ |
| || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) |
| |
| // XSI version of strerror_r. |
| #warning Not tested! |
| buffer_t buffer(200); |
| int count = _count; |
| for (;;) |
| { |
| int rc = strerror_r(err, &buffer.front(), buffer.size()); |
| if (rc == -1) |
| { |
| int _err = errno; |
| if (_err == ERANGE) |
| { |
| if (count > 0) |
| { |
| // Enlarge the buffer. |
| --count; |
| buffer.resize(buffer.size() * 2); |
| } |
| else |
| { |
| std::stringstream ostr; |
| ostr << "Error " << err << " " |
| << "(Getting error message failed: " |
| << "Buffer of " << buffer.size() |
| << " bytes is still too small" |
| << ")"; |
| return ostr.str(); |
| }; // if |
| } |
| else |
| { |
| std::stringstream ostr; |
| ostr << "Error " << err << " " |
| << "(Getting error message failed: " |
| << (level < 2 ? _err_msg(_err, level + 1) : "Oops") << ")"; |
| return ostr.str(); |
| }; // if |
| } |
| else |
| { |
| // We got the message. |
| return &buffer.front(); |
| }; // if |
| }; // forever |
| |
| #else |
| |
| // GNU version of strerror_r. |
| char buffer[2000]; |
| return strerror_r(err, buffer, sizeof(buffer)); |
| |
| #endif |
| |
| } // _err_msg |
| |
| |
| std::string dir_sep() { return "/"; } // dir_sep |
| |
| |
| std::string exe_path() |
| { |
| |
| static std::string const exe = "/proc/self/exe"; |
| |
| buffer_t path(_size); |
| int count = _count; // Max number of iterations. |
| |
| for (;;) |
| { |
| |
| ssize_t len = readlink(exe.c_str(), &path.front(), path.size()); |
| |
| if (len < 0) |
| { |
| // Oops. |
| int err = errno; |
| log_error("ERROR: Getting executable path failed: " |
| "Reading symlink `%s' failed: %s\n", |
| exe.c_str(), err_msg(err).c_str()); |
| exit(2); |
| }; // if |
| |
| if (static_cast<size_t>(len) < path.size()) |
| { |
| // We got the path. |
| path.resize(len); |
| break; |
| }; // if |
| |
| // Oops, buffer is too small. |
| if (count > 0) |
| { |
| --count; |
| // Enlarge the buffer. |
| path.resize(path.size() * 2); |
| } |
| else |
| { |
| log_error("ERROR: Getting executable path failed: " |
| "Reading symlink `%s' failed: Buffer of %lu bytes is " |
| "still too small\n", |
| exe.c_str(), (unsigned long)path.size()); |
| exit(2); |
| }; // if |
| |
| }; // forever |
| |
| return std::string(&path.front(), path.size()); |
| |
| } // exe_path |
| |
| |
| std::string exe_dir() |
| { |
| std::string path = exe_path(); |
| // We cannot pass path.c_str() to `dirname' bacause `dirname' modifies its |
| // argument. |
| buffer_t buffer(path.c_str(), |
| path.c_str() + path.size() + 1); // Copy with trailing zero. |
| return dirname(&buffer.front()); |
| } // exe_dir |
| |
| #endif // __linux__ |
| |
| // ------------------------------------------------------------------------------------------------- |
| // MS Windows |
| // ------------------------------------------------------------------------------------------------- |
| |
| #if defined(_WIN32) |
| |
| |
| #include <windows.h> |
| |
| #include <cctype> |
| #include <algorithm> |
| |
| |
| static std::string _err_msg(int err, int level) |
| { |
| |
| std::string msg; |
| |
| LPSTR buffer = NULL; |
| DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
| | FORMAT_MESSAGE_IGNORE_INSERTS; |
| |
| DWORD len = FormatMessageA(flags, NULL, err, LANG_USER_DEFAULT, |
| reinterpret_cast<LPSTR>(&buffer), 0, NULL); |
| |
| if (buffer == NULL || len == 0) |
| { |
| |
| int _err = GetLastError(); |
| char str[1024] = { 0 }; |
| snprintf(str, sizeof(str), |
| "Error 0x%08x (Getting error message failed: %s )", err, |
| (level < 2 ? _err_msg(_err, level + 1).c_str() : "Oops")); |
| msg = std::string(str); |
| } |
| else |
| { |
| |
| // Trim trailing whitespace (including `\r' and `\n'). |
| while (len > 0 && isspace(buffer[len - 1])) |
| { |
| --len; |
| }; // while |
| |
| // Drop trailing full stop. |
| if (len > 0 && buffer[len - 1] == '.') |
| { |
| --len; |
| }; // if |
| |
| msg.assign(buffer, len); |
| |
| }; // if |
| |
| if (buffer != NULL) |
| { |
| LocalFree(buffer); |
| }; // if |
| |
| return msg; |
| |
| } // _get_err_msg |
| |
| |
| std::string dir_sep() { return "\\"; } // dir_sep |
| |
| |
| std::string exe_path() |
| { |
| |
| buffer_t path(_size); |
| int count = _count; |
| |
| for (;;) |
| { |
| |
| DWORD len = GetModuleFileNameA(NULL, &path.front(), |
| static_cast<DWORD>(path.size())); |
| |
| if (len == 0) |
| { |
| int err = GetLastError(); |
| log_error("ERROR: Getting executable path failed: %s\n", |
| err_msg(err).c_str()); |
| exit(2); |
| }; // if |
| |
| if (len < path.size()) |
| { |
| path.resize(len); |
| break; |
| }; // if |
| |
| // Buffer too small. |
| if (count > 0) |
| { |
| --count; |
| path.resize(path.size() * 2); |
| } |
| else |
| { |
| log_error("ERROR: Getting executable path failed: " |
| "Buffer of %lu bytes is still too small\n", |
| (unsigned long)path.size()); |
| exit(2); |
| }; // if |
| |
| }; // forever |
| |
| return std::string(&path.front(), path.size()); |
| |
| } // exe_path |
| |
| |
| std::string exe_dir() |
| { |
| |
| std::string exe = exe_path(); |
| int count = 0; |
| |
| // Splitting path into components. |
| buffer_t drv(_MAX_DRIVE); |
| buffer_t dir(_MAX_DIR); |
| count = _count; |
| #if defined(_MSC_VER) |
| for (;;) |
| { |
| int rc = |
| _splitpath_s(exe.c_str(), &drv.front(), drv.size(), &dir.front(), |
| dir.size(), NULL, 0, // We need neither name |
| NULL, 0 // nor extension |
| ); |
| if (rc == 0) |
| { |
| break; |
| } |
| else if (rc == ERANGE) |
| { |
| if (count > 0) |
| { |
| --count; |
| // Buffer is too small, but it is not clear which one. |
| // So we have to enlarge all. |
| drv.resize(drv.size() * 2); |
| dir.resize(dir.size() * 2); |
| } |
| else |
| { |
| log_error("ERROR: Getting executable path failed: " |
| "Splitting path `%s' to components failed: " |
| "Buffers of %lu and %lu bytes are still too small\n", |
| exe.c_str(), (unsigned long)drv.size(), |
| (unsigned long)dir.size()); |
| exit(2); |
| }; // if |
| } |
| else |
| { |
| log_error("ERROR: Getting executable path failed: " |
| "Splitting path `%s' to components failed: %s\n", |
| exe.c_str(), err_msg(rc).c_str()); |
| exit(2); |
| }; // if |
| }; // forever |
| |
| #else // __MINGW32__ |
| |
| // MinGW does not have the "secure" _splitpath_s, use the insecure version |
| // instead. |
| _splitpath(exe.c_str(), &drv.front(), &dir.front(), |
| NULL, // We need neither name |
| NULL // nor extension |
| ); |
| #endif // __MINGW32__ |
| |
| // Combining components back to path. |
| // I failed with "secure" `_makepath_s'. If buffer is too small, instead of |
| // returning ERANGE, `_makepath_s' pops up dialog box and offers to debug |
| // the program. D'oh! So let us try to guess the size of result and go with |
| // insecure `_makepath'. |
| buffer_t path(std::max(drv.size() + dir.size(), size_t(_MAX_PATH)) + 10); |
| _makepath(&path.front(), &drv.front(), &dir.front(), NULL, NULL); |
| |
| return &path.front(); |
| |
| } // exe_dir |
| |
| |
| #endif // _WIN32 |
| |
| |
| std::string err_msg(int err) { return _err_msg(err, 0); } // err_msg |
| |
| |
| // ================================================================================================= |
| // C interface. |
| // ================================================================================================= |
| |
| |
| char* get_err_msg(int err) |
| { |
| char* msg = strdup(err_msg(err).c_str()); |
| CHECK_PTR(msg); |
| return msg; |
| } // get_err_msg |
| |
| |
| char* get_dir_sep() |
| { |
| char* sep = strdup(dir_sep().c_str()); |
| CHECK_PTR(sep); |
| return sep; |
| } // get_dir_sep |
| |
| |
| char* get_exe_path() |
| { |
| char* path = strdup(exe_path().c_str()); |
| CHECK_PTR(path); |
| return path; |
| } // get_exe_path |
| |
| |
| char* get_exe_dir() |
| { |
| char* dir = strdup(exe_dir().c_str()); |
| CHECK_PTR(dir); |
| return dir; |
| } // get_exe_dir |
| |
| |
| char* get_temp_filename() |
| { |
| char gFileName[256] = ""; |
| // Create a unique temporary file to allow parallel executed tests. |
| #if (defined(__linux__) || defined(__APPLE__)) && (!defined(__ANDROID__)) |
| sprintf(gFileName, "/tmp/tmpfile.XXXXXX"); |
| int fd = mkstemp(gFileName); |
| if (fd == -1) return strdup(gFileName); |
| close(fd); |
| #elif defined(_WIN32) |
| UINT ret = GetTempFileName(".", "tmp", 0, gFileName); |
| if (ret == 0) return gFileName; |
| #else |
| MTdata d = init_genrand((cl_uint)time(NULL)); |
| sprintf(gFileName, "tmpfile.%u", genrand_int32(d)); |
| #endif |
| |
| char* fn = strdup(gFileName); |
| CHECK_PTR(fn); |
| return fn; |
| } |
| |
| |
| // end of file // |