diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
commit | 9835e2ae736235810b4ea1c162ca5e65c547e770 (patch) | |
tree | 3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/iana-time-zone/src | |
parent | Releasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff) | |
download | rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip |
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/iana-time-zone/src')
-rw-r--r-- | vendor/iana-time-zone/src/ffi_utils.rs | 49 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/lib.rs | 113 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/platform.rs | 9 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/tz_android.rs | 27 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/tz_freebsd.rs | 7 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/tz_haiku.rs | 3 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/tz_illumos.rs | 22 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/tz_linux.rs | 45 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/tz_macos.rs | 144 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/tz_netbsd.rs | 25 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/tz_wasm32.rs | 21 | ||||
-rw-r--r-- | vendor/iana-time-zone/src/tz_windows.rs | 13 |
12 files changed, 478 insertions, 0 deletions
diff --git a/vendor/iana-time-zone/src/ffi_utils.rs b/vendor/iana-time-zone/src/ffi_utils.rs new file mode 100644 index 000000000..842b850e8 --- /dev/null +++ b/vendor/iana-time-zone/src/ffi_utils.rs @@ -0,0 +1,49 @@ +//! Cross platform FFI helpers. + +use std::ffi::CStr; + +// The system property named 'persist.sys.timezone' contains the name of the +// current timezone. +// +// From https://android.googlesource.com/platform/bionic/+/gingerbread-release/libc/docs/OVERVIEW.TXT#79: +// +// > The name of the current timezone is taken from the TZ environment variable, +// > if defined. Otherwise, the system property named 'persist.sys.timezone' is +// > checked instead. +const ANDROID_TIMEZONE_PROPERTY_NAME: &[u8] = b"persist.sys.timezone\0"; + +/// Return a [`CStr`] to access the timezone from an Android system properties +/// environment. +pub(crate) fn android_timezone_property_name() -> &'static CStr { + // In tests or debug mode, opt into extra runtime checks. + if cfg!(any(test, debug_assertions)) { + return CStr::from_bytes_with_nul(ANDROID_TIMEZONE_PROPERTY_NAME).unwrap(); + } + + // SAFETY: the key is NUL-terminated and there are no other NULs, this + // invariant is checked in tests. + unsafe { CStr::from_bytes_with_nul_unchecked(ANDROID_TIMEZONE_PROPERTY_NAME) } +} + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + + use super::{android_timezone_property_name, ANDROID_TIMEZONE_PROPERTY_NAME}; + + #[test] + fn test_android_timezone_property_name_is_valid_cstr() { + CStr::from_bytes_with_nul(ANDROID_TIMEZONE_PROPERTY_NAME).unwrap(); + + let mut invalid_property_name = ANDROID_TIMEZONE_PROPERTY_NAME.to_owned(); + invalid_property_name.push(b'\0'); + CStr::from_bytes_with_nul(&invalid_property_name).unwrap_err(); + } + + #[test] + fn test_android_timezone_property_name_getter() { + let key = android_timezone_property_name().to_bytes_with_nul(); + assert_eq!(key, ANDROID_TIMEZONE_PROPERTY_NAME); + std::str::from_utf8(key).unwrap(); + } +} diff --git a/vendor/iana-time-zone/src/lib.rs b/vendor/iana-time-zone/src/lib.rs new file mode 100644 index 000000000..fe4e3fb84 --- /dev/null +++ b/vendor/iana-time-zone/src/lib.rs @@ -0,0 +1,113 @@ +#![warn(clippy::all)] +#![warn(clippy::cargo)] +#![warn(clippy::undocumented_unsafe_blocks)] +#![allow(unknown_lints)] +#![warn(missing_copy_implementations)] +#![warn(missing_debug_implementations)] +#![warn(missing_docs)] +#![warn(rust_2018_idioms)] +#![warn(trivial_casts, trivial_numeric_casts)] +#![warn(unused_qualifications)] +#![warn(variant_size_differences)] + +//! get the IANA time zone for the current system +//! +//! This small utility crate provides the +//! [`get_timezone()`](fn.get_timezone.html) function. +//! +//! ```rust +//! // Get the current time zone as a string. +//! let tz_str = iana_time_zone::get_timezone()?; +//! println!("The current time zone is: {}", tz_str); +//! # Ok::<(), iana_time_zone::GetTimezoneError>(()) +//! ``` +//! +//! The resulting string can be parsed to a +//! [`chrono-tz::Tz`](https://docs.rs/chrono-tz/latest/chrono_tz/enum.Tz.html) +//! variant like this: +//! ```ignore +//! let tz_str = iana_time_zone::get_timezone()?; +//! let tz: chrono_tz::Tz = tz_str.parse()?; +//! ``` + +#[allow(dead_code)] +mod ffi_utils; + +#[cfg_attr(target_os = "linux", path = "tz_linux.rs")] +#[cfg_attr(target_os = "windows", path = "tz_windows.rs")] +#[cfg_attr(any(target_os = "macos", target_os = "ios"), path = "tz_macos.rs")] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + path = "tz_wasm32.rs" +)] +#[cfg_attr( + any(target_os = "freebsd", target_os = "dragonfly"), + path = "tz_freebsd.rs" +)] +#[cfg_attr( + any(target_os = "netbsd", target_os = "openbsd"), + path = "tz_netbsd.rs" +)] +#[cfg_attr( + any(target_os = "illumos", target_os = "solaris"), + path = "tz_illumos.rs" +)] +#[cfg_attr(target_os = "android", path = "tz_android.rs")] +#[cfg_attr(target_os = "haiku", path = "tz_haiku.rs")] +mod platform; + +/// Error types +#[derive(Debug)] +pub enum GetTimezoneError { + /// Failed to parse + FailedParsingString, + /// Wrapped IO error + IoError(std::io::Error), + /// Platform-specific error from the operating system + OsError, +} + +impl std::error::Error for GetTimezoneError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + GetTimezoneError::FailedParsingString => None, + GetTimezoneError::IoError(err) => Some(err), + GetTimezoneError::OsError => None, + } + } +} + +impl std::fmt::Display for GetTimezoneError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + f.write_str(match self { + GetTimezoneError::FailedParsingString => "GetTimezoneError::FailedParsingString", + GetTimezoneError::IoError(err) => return err.fmt(f), + GetTimezoneError::OsError => "OsError", + }) + } +} + +impl From<std::io::Error> for GetTimezoneError { + fn from(orig: std::io::Error) -> Self { + GetTimezoneError::IoError(orig) + } +} + +/// Get the current IANA time zone as a string. +/// +/// See the module-level documentation for a usage example and more details +/// about this function. +#[inline] +pub fn get_timezone() -> Result<String, GetTimezoneError> { + platform::get_timezone_inner() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn get_current() { + println!("current: {}", get_timezone().unwrap()); + } +} diff --git a/vendor/iana-time-zone/src/platform.rs b/vendor/iana-time-zone/src/platform.rs new file mode 100644 index 000000000..5992bf377 --- /dev/null +++ b/vendor/iana-time-zone/src/platform.rs @@ -0,0 +1,9 @@ +pub fn get_timezone_inner() -> std::result::Result<String, crate::GetTimezoneError> { + Err(crate::GetTimezoneError::OsError) +} + +#[cfg(not(feature = "fallback"))] +compile_error!( + "iana-time-zone is currently implemented for Linux, Window, MacOS, FreeBSD, NetBSD, \ + OpenBSD, Dragonfly, WebAssembly (browser), iOS, Illumos, Android, Solaris and Haiku.", +); diff --git a/vendor/iana-time-zone/src/tz_android.rs b/vendor/iana-time-zone/src/tz_android.rs new file mode 100644 index 000000000..27255b52f --- /dev/null +++ b/vendor/iana-time-zone/src/tz_android.rs @@ -0,0 +1,27 @@ +use std::sync::Once; + +use android_system_properties::AndroidSystemProperties; + +use crate::ffi_utils::android_timezone_property_name; + +pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> { + let key = android_timezone_property_name(); + + get_properties() + .and_then(|properties| properties.get_from_cstr(key)) + .ok_or(crate::GetTimezoneError::OsError) +} + +fn get_properties() -> Option<&'static AndroidSystemProperties> { + static INITIALIZED: Once = Once::new(); + static mut PROPERTIES: Option<AndroidSystemProperties> = None; + + INITIALIZED.call_once(|| { + let properties = AndroidSystemProperties::new(); + // SAFETY: `INITIALIZED` is synchronizing. The variable is only assigned to once. + unsafe { PROPERTIES = Some(properties) }; + }); + + // SAFETY: `INITIALIZED` is synchronizing. The variable is only assigned to once. + unsafe { PROPERTIES.as_ref() } +} diff --git a/vendor/iana-time-zone/src/tz_freebsd.rs b/vendor/iana-time-zone/src/tz_freebsd.rs new file mode 100644 index 000000000..4d55e1536 --- /dev/null +++ b/vendor/iana-time-zone/src/tz_freebsd.rs @@ -0,0 +1,7 @@ +pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> { + // see https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/19 + let mut contents = std::fs::read_to_string("/var/db/zoneinfo")?; + // Trim to the correct length without allocating. + contents.truncate(contents.trim_end().len()); + Ok(contents) +} diff --git a/vendor/iana-time-zone/src/tz_haiku.rs b/vendor/iana-time-zone/src/tz_haiku.rs new file mode 100644 index 000000000..d78372b6d --- /dev/null +++ b/vendor/iana-time-zone/src/tz_haiku.rs @@ -0,0 +1,3 @@ +pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> { + iana_time_zone_haiku::get_timezone().ok_or(crate::GetTimezoneError::OsError) +} diff --git a/vendor/iana-time-zone/src/tz_illumos.rs b/vendor/iana-time-zone/src/tz_illumos.rs new file mode 100644 index 000000000..17b099b10 --- /dev/null +++ b/vendor/iana-time-zone/src/tz_illumos.rs @@ -0,0 +1,22 @@ +use std::fs::OpenOptions; +use std::io::{BufRead, BufReader}; + +pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> { + // https://illumos.org/man/5/TIMEZONE + // https://docs.oracle.com/cd/E23824_01/html/821-1473/uc-timezone-4.html + + let file = OpenOptions::new().read(true).open("/etc/default/init")?; + let mut reader = BufReader::with_capacity(1536, file); + let mut line = String::with_capacity(80); + loop { + line.clear(); + let count = reader.read_line(&mut line)?; + if count == 0 { + return Err(crate::GetTimezoneError::FailedParsingString); + } else if line.starts_with("TZ=") { + line.truncate(line.trim_end().len()); + line.replace_range(..3, ""); + return Ok(line); + } + } +} diff --git a/vendor/iana-time-zone/src/tz_linux.rs b/vendor/iana-time-zone/src/tz_linux.rs new file mode 100644 index 000000000..c1133e1cd --- /dev/null +++ b/vendor/iana-time-zone/src/tz_linux.rs @@ -0,0 +1,45 @@ +use std::fs::{read_link, read_to_string}; + +pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> { + etc_localtime().or_else(|_| etc_timezone()) +} + +fn etc_timezone() -> Result<String, crate::GetTimezoneError> { + // see https://stackoverflow.com/a/12523283 + let mut contents = read_to_string("/etc/timezone")?; + // Trim to the correct length without allocating. + contents.truncate(contents.trim_end().len()); + Ok(contents) +} + +fn etc_localtime() -> Result<String, crate::GetTimezoneError> { + // Per <https://www.man7.org/linux/man-pages/man5/localtime.5.html>: + // “ The /etc/localtime file configures the system-wide timezone of the local system that is + // used by applications for presentation to the user. It should be an absolute or relative + // symbolic link pointing to /usr/share/zoneinfo/, followed by a timezone identifier such as + // "Europe/Berlin" or "Etc/UTC". The resulting link should lead to the corresponding binary + // tzfile(5) timezone data for the configured timezone. ” + + // Systemd does not canonicalize the link, but only checks if it is prefixed by + // "/usr/share/zoneinfo/" or "../usr/share/zoneinfo/". So we do the same. + // <https://github.com/systemd/systemd/blob/9102c625a673a3246d7e73d8737f3494446bad4e/src/basic/time-util.c#L1493> + + const PREFIXES: &[&str] = &[ + "/usr/share/zoneinfo/", // absolute path + "../usr/share/zoneinfo/", // relative path + "/etc/zoneinfo/", // absolute path for NixOS + "../etc/zoneinfo/", // relative path for NixOS + ]; + let mut s = read_link("/etc/localtime")? + .into_os_string() + .into_string() + .map_err(|_| crate::GetTimezoneError::FailedParsingString)?; + for &prefix in PREFIXES { + if s.starts_with(prefix) { + // Trim to the correct length without allocating. + s.replace_range(..prefix.len(), ""); + return Ok(s); + } + } + Err(crate::GetTimezoneError::FailedParsingString) +} diff --git a/vendor/iana-time-zone/src/tz_macos.rs b/vendor/iana-time-zone/src/tz_macos.rs new file mode 100644 index 000000000..70c39e83f --- /dev/null +++ b/vendor/iana-time-zone/src/tz_macos.rs @@ -0,0 +1,144 @@ +pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> { + get_timezone().ok_or(crate::GetTimezoneError::OsError) +} + +#[inline] +fn get_timezone() -> Option<String> { + // The longest name in the IANA time zone database is 25 ASCII characters long. + const MAX_LEN: usize = 32; + let mut buf = [0; MAX_LEN]; + + // Get system time zone, and borrow its name. + let tz = system_time_zone::SystemTimeZone::new()?; + let name = tz.name()?; + + // If the name is encoded in UTF-8, copy it directly. + let name = if let Some(name) = name.as_utf8() { + name + } else { + // Otherwise convert the name to UTF-8. + name.to_utf8(&mut buf)? + }; + + if name.is_empty() || name.len() >= MAX_LEN { + // The name should not be empty, or excessively long. + None + } else { + Some(name.to_owned()) + } +} + +mod system_time_zone { + //! create a safe wrapper around `CFTimeZoneRef` + + use core_foundation_sys::base::{CFRelease, CFTypeRef}; + use core_foundation_sys::timezone::{CFTimeZoneCopySystem, CFTimeZoneGetName, CFTimeZoneRef}; + + pub(crate) struct SystemTimeZone(CFTimeZoneRef); + + impl Drop for SystemTimeZone { + fn drop(&mut self) { + // SAFETY: `SystemTimeZone` is only ever created with a valid `CFTimeZoneRef`. + unsafe { CFRelease(self.0 as CFTypeRef) }; + } + } + + impl SystemTimeZone { + pub(crate) fn new() -> Option<Self> { + // SAFETY: No invariants to uphold. We'll release the pointer when we don't need it anymore. + let v: CFTimeZoneRef = unsafe { CFTimeZoneCopySystem() }; + if v.is_null() { + None + } else { + Some(SystemTimeZone(v)) + } + } + + /// Get the time zone name as a [super::string_ref::StringRef]. + /// + /// The lifetime of the `StringRef` is bound to our lifetime. Mutable + /// access is also prevented by taking a reference to `self`. + pub(crate) fn name(&self) -> Option<super::string_ref::StringRef<'_, Self>> { + // SAFETY: `SystemTimeZone` is only ever created with a valid `CFTimeZoneRef`. + let string = unsafe { CFTimeZoneGetName(self.0) }; + if string.is_null() { + None + } else { + // SAFETY: here we ensure that `string` is a valid pointer. + Some(unsafe { super::string_ref::StringRef::new(string, self) }) + } + } + } +} + +mod string_ref { + //! create safe wrapper around `CFStringRef` + + use std::convert::TryInto; + + use core_foundation_sys::base::{Boolean, CFRange}; + use core_foundation_sys::string::{ + kCFStringEncodingUTF8, CFStringGetBytes, CFStringGetCStringPtr, CFStringGetLength, + CFStringRef, + }; + + pub(crate) struct StringRef<'a, T> { + string: CFStringRef, + // We exclude mutable access to the parent by taking a reference to the + // parent (rather than, for example, just using a marker to enforce the + // parent's lifetime). + _parent: &'a T, + } + + impl<'a, T> StringRef<'a, T> { + // SAFETY: `StringRef` must be valid pointer + pub(crate) unsafe fn new(string: CFStringRef, _parent: &'a T) -> Self { + Self { string, _parent } + } + + pub(crate) fn as_utf8(&self) -> Option<&'a str> { + // SAFETY: `StringRef` is only ever created with a valid `CFStringRef`. + let v = unsafe { CFStringGetCStringPtr(self.string, kCFStringEncodingUTF8) }; + if !v.is_null() { + // SAFETY: `CFStringGetCStringPtr()` returns NUL-terminated strings. + let v = unsafe { std::ffi::CStr::from_ptr(v) }; + if let Ok(v) = v.to_str() { + return Some(v); + } + } + None + } + + pub(crate) fn to_utf8<'b>(&self, buf: &'b mut [u8]) -> Option<&'b str> { + // SAFETY: `StringRef` is only ever created with a valid `CFStringRef`. + let length = unsafe { CFStringGetLength(self.string) }; + + let mut buf_bytes = 0; + let range = CFRange { + location: 0, + length, + }; + + let converted_bytes = unsafe { + // SAFETY: `StringRef` is only ever created with a valid `CFStringRef`. + CFStringGetBytes( + self.string, + range, + kCFStringEncodingUTF8, + b'\0', + false as Boolean, + buf.as_mut_ptr(), + buf.len() as isize, + &mut buf_bytes, + ) + }; + if converted_bytes != length { + return None; + } + + let len = buf_bytes.try_into().ok()?; + let s = buf.get(..len)?; + std::str::from_utf8(s).ok() + } + } +} diff --git a/vendor/iana-time-zone/src/tz_netbsd.rs b/vendor/iana-time-zone/src/tz_netbsd.rs new file mode 100644 index 000000000..84cf8b05e --- /dev/null +++ b/vendor/iana-time-zone/src/tz_netbsd.rs @@ -0,0 +1,25 @@ +use std::fs::read_link; + +pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> { + // see https://www.cyberciti.biz/faq/openbsd-time-zone-howto/ + + // This is a backport of the Linux implementation. + // NetBSDs is less than thorough how the softlink should be set up. + + const PREFIXES: &[&str] = &[ + "/usr/share/zoneinfo/", // absolute path + "../usr/share/zoneinfo/", // relative path + ]; + let mut s = read_link("/etc/localtime")? + .into_os_string() + .into_string() + .map_err(|_| crate::GetTimezoneError::FailedParsingString)?; + for &prefix in PREFIXES { + if s.starts_with(prefix) { + // Trim to the correct length without allocating. + s.replace_range(..prefix.len(), ""); + return Ok(s); + } + } + Err(crate::GetTimezoneError::FailedParsingString) +} diff --git a/vendor/iana-time-zone/src/tz_wasm32.rs b/vendor/iana-time-zone/src/tz_wasm32.rs new file mode 100644 index 000000000..69c36b553 --- /dev/null +++ b/vendor/iana-time-zone/src/tz_wasm32.rs @@ -0,0 +1,21 @@ +use js_sys::{Array, Intl, Object, Reflect}; +use wasm_bindgen::JsValue; + +pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> { + let intl = Intl::DateTimeFormat::new(&Array::new(), &Object::new()).resolved_options(); + Reflect::get(&intl, &JsValue::from_str("timeZone")) + .ok() + .and_then(|tz| tz.as_string()) + .ok_or(crate::GetTimezoneError::OsError) +} + +#[cfg(test)] +mod tests { + use wasm_bindgen_test::*; + + #[wasm_bindgen_test] + fn pass() { + let tz = super::get_timezone_inner().unwrap(); + console_log!("tz={:?}", tz); + } +} diff --git a/vendor/iana-time-zone/src/tz_windows.rs b/vendor/iana-time-zone/src/tz_windows.rs new file mode 100644 index 000000000..eaa5d5a48 --- /dev/null +++ b/vendor/iana-time-zone/src/tz_windows.rs @@ -0,0 +1,13 @@ +use windows::Globalization::Calendar; + +impl From<windows::core::Error> for crate::GetTimezoneError { + fn from(orig: windows::core::Error) -> Self { + crate::GetTimezoneError::IoError(std::io::Error::new(std::io::ErrorKind::Other, orig)) + } +} + +pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> { + let cal = Calendar::new()?; + let tz_hstring = cal.GetTimeZone()?; + Ok(tz_hstring.to_string()) +} |