Do a runtime test for realpath vs readlink

* Ubuntu needs to use readlink under current_zone
  and most everyone else needs realpath.  Attempting
  to make everyone happy with this commit.
This commit is contained in:
Howard Hinnant 2020-01-24 20:23:30 -05:00
parent 9502bc27a3
commit 5f49afc1e9

View File

@ -3673,6 +3673,56 @@ tzdb::current_zone() const
#else // !_WIN32
#if HAS_STRING_VIEW
static
std::string_view
extract_tz_name(char const* rp)
{
using namespace std;
string_view result = rp;
CONSTDATA string_view zoneinfo = "zoneinfo";
size_t pos = result.rfind(zoneinfo);
if (pos == result.npos)
throw runtime_error(
"current_zone() failed to find \"zoneinfo\" in " + string(result));
pos = result.find('/', pos);
result.remove_prefix(pos + 1);
return result;
}
#else // !HAS_STRING_VIEW
static
std::string
extract_tz_name(char const* rp)
{
using namespace std;
string result = rp;
CONSTDATA char zoneinfo[] = "zoneinfo";
size_t pos = result.rfind(zoneinfo);
if (pos == result.npos)
throw runtime_error(
"current_zone() failed to find \"zoneinfo\" in " + result);
pos = result.find('/', pos);
result.erase(0, pos + 1);
return result;
}
#endif // HAS_STRING_VIEW
static
bool
sniff_realpath(const char* timezone)
{
using namespace std;
char rp[PATH_MAX+1] = {};
if (realpath(timezone, rp) == nullptr)
throw system_error(errno, system_category(), "realpath() failed");
auto result = extract_tz_name(rp);
return result != "posixrules";
}
const time_zone*
tzdb::current_zone() const
{
@ -3692,31 +3742,22 @@ tzdb::current_zone() const
{
struct stat sb;
CONSTDATA auto timezone = "/etc/localtime";
if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) {
if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0)
{
using namespace std;
static const bool use_realpath = sniff_realpath(timezone);
char rp[PATH_MAX+1] = {};
if (realpath(timezone, rp) == nullptr)
throw system_error(errno, system_category(), "realpath() failed");
#if HAS_STRING_VIEW
string_view result = rp;
CONSTDATA string_view zoneinfo = "zoneinfo";
size_t pos = result.rfind(zoneinfo);
if (pos == result.npos)
throw runtime_error(
"current_zone() failed to find \"zoneinfo\" in " + string(result));
pos = result.find('/', pos);
result.remove_prefix(pos + 1);
#else
string result = rp;
CONSTDATA char zoneinfo[] = "zoneinfo";
size_t pos = result.rfind(zoneinfo);
if (pos == result.npos)
throw runtime_error(
"current_zone() failed to find \"zoneinfo\" in " + result);
pos = result.find('/', pos);
result.erase(0, pos + 1);
#endif
return locate_zone(result);
if (use_realpath)
{
if (realpath(timezone, rp) == nullptr)
throw system_error(errno, system_category(), "realpath() failed");
}
else
{
if (readlink(timezone, rp, sizeof(rp)-1) <= 0)
throw system_error(errno, system_category(), "readlink() failed");
}
return locate_zone(extract_tz_name(rp));
}
}
// On embedded systems e.g. buildroot with uclibc the timezone is linked