summaryrefslogtreecommitdiffstats
path: root/vendor/iana-time-zone/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/iana-time-zone/src')
-rw-r--r--vendor/iana-time-zone/src/platform.rs2
-rw-r--r--vendor/iana-time-zone/src/tz_linux.rs141
2 files changed, 141 insertions, 2 deletions
diff --git a/vendor/iana-time-zone/src/platform.rs b/vendor/iana-time-zone/src/platform.rs
index 5992bf377..d5ba0ee34 100644
--- a/vendor/iana-time-zone/src/platform.rs
+++ b/vendor/iana-time-zone/src/platform.rs
@@ -1,4 +1,4 @@
-pub fn get_timezone_inner() -> std::result::Result<String, crate::GetTimezoneError> {
+pub fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
Err(crate::GetTimezoneError::OsError)
}
diff --git a/vendor/iana-time-zone/src/tz_linux.rs b/vendor/iana-time-zone/src/tz_linux.rs
index c1133e1cd..c0548ec09 100644
--- a/vendor/iana-time-zone/src/tz_linux.rs
+++ b/vendor/iana-time-zone/src/tz_linux.rs
@@ -1,7 +1,9 @@
use std::fs::{read_link, read_to_string};
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
- etc_localtime().or_else(|_| etc_timezone())
+ etc_localtime()
+ .or_else(|_| etc_timezone())
+ .or_else(|_| openwrt::etc_config_system())
}
fn etc_timezone() -> Result<String, crate::GetTimezoneError> {
@@ -43,3 +45,140 @@ fn etc_localtime() -> Result<String, crate::GetTimezoneError> {
}
Err(crate::GetTimezoneError::FailedParsingString)
}
+
+mod openwrt {
+ use std::io::BufRead;
+ use std::{fs, io, iter};
+
+ pub(crate) fn etc_config_system() -> Result<String, crate::GetTimezoneError> {
+ let f = fs::OpenOptions::new()
+ .read(true)
+ .open("/etc/config/system")?;
+ let mut f = io::BufReader::new(f);
+ let mut in_system_section = false;
+ let mut line = String::with_capacity(80);
+
+ // prefer option "zonename" (IANA time zone) over option "timezone" (POSIX time zone)
+ let mut timezone = None;
+ loop {
+ line.clear();
+ f.read_line(&mut line)?;
+ if line.is_empty() {
+ break;
+ }
+
+ let mut iter = IterWords(&line);
+ let mut next = || iter.next().transpose();
+
+ if let Some(keyword) = next()? {
+ if keyword == "config" {
+ in_system_section = next()? == Some("system") && next()?.is_none();
+ } else if in_system_section && keyword == "option" {
+ if let Some(key) = next()? {
+ if key == "zonename" {
+ if let (Some(zonename), None) = (next()?, next()?) {
+ return Ok(zonename.to_owned());
+ }
+ } else if key == "timezone" {
+ if let (Some(value), None) = (next()?, next()?) {
+ timezone = Some(value.to_owned());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ timezone.ok_or_else(|| crate::GetTimezoneError::OsError)
+ }
+
+ #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+ struct BrokenQuote;
+
+ impl From<BrokenQuote> for crate::GetTimezoneError {
+ fn from(_: BrokenQuote) -> Self {
+ crate::GetTimezoneError::FailedParsingString
+ }
+ }
+
+ /// Iterated over all words in a OpenWRT config line.
+ struct IterWords<'a>(&'a str);
+
+ impl<'a> Iterator for IterWords<'a> {
+ type Item = Result<&'a str, BrokenQuote>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match read_word(self.0) {
+ Ok(Some((item, tail))) => {
+ self.0 = tail;
+ Some(Ok(item))
+ }
+ Ok(None) => {
+ self.0 = "";
+ None
+ }
+ Err(err) => {
+ self.0 = "";
+ Some(Err(err))
+ }
+ }
+ }
+ }
+
+ impl iter::FusedIterator for IterWords<'_> {}
+
+ /// Read the next word in a OpenWRT config line. Strip any surrounding quotation marks.
+ ///
+ /// Returns
+ ///
+ /// * a tuple `Some((word, remaining_line))` if found,
+ /// * `None` if the line is exhausted, or
+ /// * `Err(BrokenQuote)` if the line could not be parsed.
+ #[allow(clippy::manual_strip)] // needs to be compatile to 1.36
+ fn read_word(s: &str) -> Result<Option<(&str, &str)>, BrokenQuote> {
+ let s = s.trim_start();
+ if s.is_empty() || s.starts_with('#') {
+ Ok(None)
+ } else if s.starts_with('\'') {
+ let mut iter = s[1..].splitn(2, '\'');
+ match (iter.next(), iter.next()) {
+ (Some(item), Some(tail)) => Ok(Some((item, tail))),
+ _ => Err(BrokenQuote),
+ }
+ } else if s.starts_with('"') {
+ let mut iter = s[1..].splitn(2, '"');
+ match (iter.next(), iter.next()) {
+ (Some(item), Some(tail)) => Ok(Some((item, tail))),
+ _ => Err(BrokenQuote),
+ }
+ } else {
+ let mut iter = s.splitn(2, |c: char| c.is_whitespace());
+ match (iter.next(), iter.next()) {
+ (Some(item), Some(tail)) => Ok(Some((item, tail))),
+ _ => Ok(Some((s, ""))),
+ }
+ }
+ }
+
+ #[cfg(test)]
+ #[test]
+ fn test_read_word() {
+ assert_eq!(
+ read_word(" option timezone 'CST-8'\n").unwrap(),
+ Some(("option", "timezone 'CST-8'\n")),
+ );
+ assert_eq!(
+ read_word("timezone 'CST-8'\n").unwrap(),
+ Some(("timezone", "'CST-8'\n")),
+ );
+ assert_eq!(read_word("'CST-8'\n").unwrap(), Some(("CST-8", "\n")));
+ assert_eq!(read_word("\n").unwrap(), None);
+
+ assert_eq!(
+ read_word(r#""time 'Zone'""#).unwrap(),
+ Some(("time 'Zone'", "")),
+ );
+
+ assert_eq!(read_word("'CST-8").unwrap_err(), BrokenQuote);
+ }
+}