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/tz_macos.rs | |
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/tz_macos.rs')
-rw-r--r-- | vendor/iana-time-zone/src/tz_macos.rs | 144 |
1 files changed, 144 insertions, 0 deletions
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() + } + } +} |