#include "os_helpers.h"
#include "errorHelpers.h"
#include <cerrno>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <vector>
#if defined(__ANDROID__)
#include <android/api-level.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;
int const _count = 8;
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#include <libgen.h>
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.
*/
buffer_t buffer(100);
int count = _count;
for (;;)
{
int rc = strerror_r(err, &buffer.front(), buffer.size());
if (rc == EINVAL)
{
return &buffer.front();
}
else if (rc == ERANGE)
{
if (count > 0)
{
--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();
};
}
else if (rc == 0)
{
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();
};
};
}
std::string dir_sep() { return "/"; }
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 (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);
};
};
return &path.front();
}
std::string exe_dir()
{
std::string path = exe_path();
buffer_t buffer(path.c_str(),
path.c_str() + path.size() + 1);
return dirname(&buffer.front());
}
#endif
#if defined(__linux__)
#include <cerrno>
#include <libgen.h>
#include <unistd.h>
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)
#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)
{
--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();
};
}
else
{
std::stringstream ostr;
ostr << "Error " << err << " "
<< "(Getting error message failed: "
<< (level < 2 ? _err_msg(_err, level + 1) : "Oops") << ")";
return ostr.str();
};
}
else
{
return &buffer.front();
};
};
#else
char buffer[2000];
return strerror_r(err, buffer, sizeof(buffer));
#endif
}
std::string dir_sep() { return "/"; }
std::string exe_path()
{
static std::string const exe = "/proc/self/exe";
buffer_t path(_size);
int count = _count;
for (;;)
{
ssize_t len = readlink(exe.c_str(), &path.front(), path.size());
if (len < 0)
{
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 (len < path.size())
{
path.resize(len);
break;
};
if (count > 0)
{
--count;
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);
};
};
return std::string(&path.front(), path.size());
}
std::string exe_dir()
{
std::string path = exe_path();
buffer_t buffer(path.c_str(),
path.c_str() + path.size() + 1);
return dirname(&buffer.front());
}
#endif
#if defined(_WIN32)
#include <windows.h>
#if defined(max)
#undef max
#endif
#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
{
while (len > 0 && isspace(buffer[len - 1]))
{
--len;
};
if (len > 0 && buffer[len - 1] == '.')
{
--len;
};
msg.assign(buffer, len);
};
if (buffer != NULL)
{
LocalFree(buffer);
};
return msg;
}
std::string dir_sep() { return "\\"; }
std::string exe_path()
{
buffer_t path(_size);
int count = _count;
for (;;)
{
DWORD len = GetModuleFileNameA(NULL, &path.front(), 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 (len < path.size())
{
path.resize(len);
break;
};
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);
};
};
return std::string(&path.front(), path.size());
}
std::string exe_dir()
{
std::string exe = exe_path();
int count = 0;
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,
NULL, 0
);
if (rc == 0)
{
break;
}
else if (rc == ERANGE)
{
if (count > 0)
{
--count;
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);
};
}
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);
};
};
#else
_splitpath(exe.c_str(), &drv.front(), &dir.front(),
NULL,
NULL
);
#endif
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();
}
#endif
std::string err_msg(int err) { return _err_msg(err, 0); }
char* get_err_msg(int err)
{
char* msg = strdup(err_msg(err).c_str());
CHECK_PTR(msg);
return msg;
}
char* get_dir_sep()
{
char* sep = strdup(dir_sep().c_str());
CHECK_PTR(sep);
return sep;
}
char* get_exe_path()
{
char* path = strdup(exe_path().c_str());
CHECK_PTR(path);
return path;
}
char* get_exe_dir()
{
char* dir = strdup(exe_dir().c_str());
CHECK_PTR(dir);
return dir;
}