diff options
Diffstat (limited to 'absl/time/internal/cctz/src/time_zone_info.cc')
-rw-r--r-- | absl/time/internal/cctz/src/time_zone_info.cc | 104 |
1 files changed, 83 insertions, 21 deletions
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc index 8039353e..4f175d95 100644 --- a/absl/time/internal/cctz/src/time_zone_info.cc +++ b/absl/time/internal/cctz/src/time_zone_info.cc @@ -39,10 +39,12 @@ #include <cstdio> #include <cstdlib> #include <cstring> +#include <fstream> #include <functional> #include <memory> #include <sstream> #include <string> +#include <utility> #include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" @@ -576,14 +578,17 @@ bool TimeZoneInfo::Load(ZoneInfoSource* zip) { namespace { +using FilePtr = std::unique_ptr<FILE, int (*)(FILE*)>; + // fopen(3) adaptor. -inline FILE* FOpen(const char* path, const char* mode) { +inline FilePtr FOpen(const char* path, const char* mode) { #if defined(_MSC_VER) FILE* fp; if (fopen_s(&fp, path, mode) != 0) fp = nullptr; - return fp; + return FilePtr(fp, fclose); #else - return fopen(path, mode); // TODO: Enable the close-on-exec flag. + // TODO: Enable the close-on-exec flag. + return FilePtr(fopen(path, mode), fclose); #endif } @@ -611,11 +616,11 @@ class FileZoneInfoSource : public ZoneInfoSource { protected: explicit FileZoneInfoSource( - FILE* fp, std::size_t len = std::numeric_limits<std::size_t>::max()) - : fp_(fp, fclose), len_(len) {} + FilePtr fp, std::size_t len = std::numeric_limits<std::size_t>::max()) + : fp_(std::move(fp)), len_(len) {} private: - std::unique_ptr<FILE, int (*)(FILE*)> fp_; + FilePtr fp_; std::size_t len_; }; @@ -644,17 +649,9 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open( path.append(name, pos, std::string::npos); // Open the zoneinfo file. - FILE* fp = FOpen(path.c_str(), "rb"); + auto fp = FOpen(path.c_str(), "rb"); if (fp == nullptr) return nullptr; - std::size_t length = 0; - if (fseek(fp, 0, SEEK_END) == 0) { - long offset = ftell(fp); - if (offset >= 0) { - length = static_cast<std::size_t>(offset); - } - rewind(fp); - } - return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length)); + return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(std::move(fp))); } class AndroidZoneInfoSource : public FileZoneInfoSource { @@ -663,8 +660,9 @@ class AndroidZoneInfoSource : public FileZoneInfoSource { std::string Version() const override { return version_; } private: - explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers) - : FileZoneInfoSource(fp, len), version_(vers) {} + explicit AndroidZoneInfoSource(FilePtr fp, std::size_t len, + std::string version) + : FileZoneInfoSource(std::move(fp), len), version_(std::move(version)) {} std::string version_; }; @@ -676,8 +674,8 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( // See Android's libc/tzcode/bionic.cpp for additional information. for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", "/system/usr/share/zoneinfo/tzdata"}) { - std::unique_ptr<FILE, int (*)(FILE*)> fp(FOpen(tzdata, "rb"), fclose); - if (fp.get() == nullptr) continue; + auto fp = FOpen(tzdata, "rb"); + if (fp == nullptr) continue; char hbuf[24]; // covers header.zonetab_offset too if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue; @@ -703,7 +701,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( if (strcmp(name.c_str() + pos, ebuf) == 0) { if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break; return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource( - fp.release(), static_cast<std::size_t>(length), vers)); + std::move(fp), static_cast<std::size_t>(length), vers)); } } } @@ -711,6 +709,69 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( return nullptr; } +// A zoneinfo source for use inside Fuchsia components. This attempts to +// read zoneinfo files from one of several known paths in a component's +// incoming namespace. [Config data][1] is preferred, but package-specific +// resources are also supported. +// +// Fuchsia's implementation supports `FileZoneInfoSource::Version()`. +// +// [1]: +// https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component +class FuchsiaZoneInfoSource : public FileZoneInfoSource { + public: + static std::unique_ptr<ZoneInfoSource> Open(const std::string& name); + std::string Version() const override { return version_; } + + private: + explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version) + : FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {} + std::string version_; +}; + +std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open( + const std::string& name) { + // Use of the "file:" prefix is intended for testing purposes only. + const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0; + + // Prefixes where a Fuchsia component might find zoneinfo files, + // in descending order of preference. + const auto kTzdataPrefixes = { + "/config/data/tzdata/", + "/pkg/data/tzdata/", + "/data/tzdata/", + }; + const auto kEmptyPrefix = {""}; + const bool name_absolute = (pos != name.size() && name[pos] == '/'); + const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes; + + // Fuchsia builds place zoneinfo files at "<prefix><format><name>". + for (const std::string prefix : prefixes) { + std::string path = prefix; + if (!prefix.empty()) path += "zoneinfo/tzif2/"; // format + path.append(name, pos, std::string::npos); + + auto fp = FOpen(path.c_str(), "rb"); + if (fp == nullptr) continue; + + std::string version; + if (!prefix.empty()) { + // Fuchsia builds place the version in "<prefix>revision.txt". + std::ifstream version_stream(prefix + "revision.txt"); + if (version_stream.is_open()) { + // revision.txt should contain no newlines, but to be + // defensive we read just the first line. + std::getline(version_stream, version); + } + } + + return std::unique_ptr<ZoneInfoSource>( + new FuchsiaZoneInfoSource(std::move(fp), std::move(version))); + } + + return nullptr; +} + } // namespace bool TimeZoneInfo::Load(const std::string& name) { @@ -728,6 +789,7 @@ bool TimeZoneInfo::Load(const std::string& name) { name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> { if (auto z = FileZoneInfoSource::Open(n)) return z; if (auto z = AndroidZoneInfoSource::Open(n)) return z; + if (auto z = FuchsiaZoneInfoSource::Open(n)) return z; return nullptr; }); return zip != nullptr && Load(zip.get()); |