From cf106906b5351c106d05dbb6c7d8506c267a6179 Mon Sep 17 00:00:00 2001
From: Guillaume Gielly <guillaume@dialup.fr>
Date: Sun, 5 Jan 2025 22:30:35 +0100
Subject: [PATCH] Code cleanup

---
 .vscode/settings.json       |   3 +
 src/header/time/strptime.rs | 725 ++++++++++++++++++------------------
 2 files changed, 372 insertions(+), 356 deletions(-)
 create mode 100644 .vscode/settings.json

diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..9ddf6b280
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+    "cmake.ignoreCMakeListsMissing": true
+}
\ No newline at end of file
diff --git a/src/header/time/strptime.rs b/src/header/time/strptime.rs
index 5ffaf94b2..aa3972c38 100644
--- a/src/header/time/strptime.rs
+++ b/src/header/time/strptime.rs
@@ -1,14 +1,18 @@
 // https://pubs.opengroup.org/onlinepubs/7908799/xsh/strptime.html
 
+use crate::{
+    header::{string::strlen, time::tm},
+    platform::types::size_t,
+};
 use alloc::{string::String, vec::Vec};
 use core::{
-    ffi::{c_char, c_int},
+    ffi::{c_char, c_int, c_void, CStr},
     mem::MaybeUninit,
-    ptr, slice, str,
+    ptr,
+    ptr::NonNull,
+    slice, str,
 };
 
-use crate::{header::time::tm, platform::types::size_t};
-
 /// For convenience, we define some helper constants for the C-locale.
 static SHORT_DAYS: [&str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
 static LONG_DAYS: [&str; 7] = [
@@ -38,418 +42,428 @@ static LONG_MONTHS: [&str; 12] = [
     "December",
 ];
 
-// Macro for matching a character ignoring ASCII case
-macro_rules! eq_icase {
-    ($c1:expr, $c2:expr) => {
-        $c1.eq_ignore_ascii_case(&$c2)
+#[no_mangle]
+pub unsafe extern "C" fn strptime(
+    buf: *const c_char,
+    format: *const c_char,
+    tm: *mut tm,
+) -> *mut c_char {
+    // Validate inputs
+    let buf_ptr = if let Some(ptr) = NonNull::new(buf as *const c_void as *mut c_void) {
+        ptr
+    } else {
+        return ptr::null_mut();
+    };
+    //
+    let fmt_ptr = if let Some(ptr) = NonNull::new(format as *const c_void as *mut c_void) {
+        ptr
+    } else {
+        return ptr::null_mut();
     };
-}
 
-#[no_mangle]
-pub extern "C" fn strptime(buf: *const c_char, format: *const c_char, tm: *mut tm) -> *mut c_char {
-    unsafe {
-        if buf.is_null() || format.is_null() || tm.is_null() {
+    let tm_ptr = if let Some(ptr) = NonNull::new(tm) {
+        ptr
+    } else {
+        return ptr::null_mut();
+    };
+
+    // Convert raw pointers into slices/strings.
+    let input_str = unsafe {
+        if buf.is_null() {
             return ptr::null_mut();
         }
+        match CStr::from_ptr(buf).to_str() {
+            Ok(s) => s,
+            Err(_) => return ptr::null_mut(), // Not a valid UTF-8
+        }
+    };
+
+    let fmt_str = unsafe {
+        if format.is_null() {
+            return ptr::null_mut();
+        }
+        match CStr::from_ptr(format).to_str() {
+            Ok(s) => s,
+            Err(_) => return ptr::null_mut(), // Not a valid UTF-8
+        }
+    };
 
-        // Convert raw pointers into Rust slices/strings.
-        let mut input_str = cstr_to_str(buf);
-        let fmt_str = cstr_to_str(format);
+    // Zero-initialize the output `tm` structure
+    // (equivalent to: tm_sec=0, tm_min=0, tm_hour=0...)
+    ptr::write_bytes(tm, 0, 1);
 
-        // Zero-initialize the output `tm` structure
-        // (equivalent to: tm_sec=0, tm_min=0, tm_hour=0, etc.)
-        ptr::write_bytes(tm, 0, 1);
+    // We parse the format specifiers in a loop
+    let mut fmt_chars = fmt_str.chars().peekable();
+    let mut index_in_input = 0;
 
-        // We parse the format specifiers in a loop
-        let mut fmt_chars = fmt_str.chars().peekable();
-        let mut index_in_input = 0;
+    while let Some(fc) = fmt_chars.next() {
+        if fc != '%' {
+            // If it's a normal character, we expect it to match exactly in input
+            if input_str.len() <= index_in_input {
+                return ptr::null_mut(); // input ended too soon
+            }
+            let in_char = input_str.as_bytes()[index_in_input] as char;
+            if in_char != fc {
+                // mismatch
+                return ptr::null_mut();
+            }
+            index_in_input += 1;
+            continue;
+        }
 
-        while let Some(fc) = fmt_chars.next() {
-            if fc != '%' {
-                // If it's a normal character, we expect it to match exactly in input
-                if input_str.len() <= index_in_input {
-                    return ptr::null_mut(); // input ended too soon
+        // If we see '%', read the next character
+        let Some(spec) = fmt_chars.next() else {
+            // format string ended abruptly after '%'
+            return ptr::null_mut();
+        };
+
+        // POSIX says `%E` or `%O` are modified specifiers for locale.
+        // We will skip them if they appear (like strftime does) and read the next char.
+        let final_spec = if spec == 'E' || spec == 'O' {
+            match fmt_chars.next() {
+                Some(ch) => ch,
+                None => return ptr::null_mut(),
+            }
+        } else {
+            spec
+        };
+
+        // Handle known specifiers
+        match final_spec {
+            ///////////////////////////
+            // Whitespace: %n or %t  //
+            ///////////////////////////
+            'n' | 't' => {
+                // Skip over any whitespace in the input
+                while index_in_input < input_str.len()
+                    && input_str.as_bytes()[index_in_input].is_ascii_whitespace()
+                {
+                    index_in_input += 1;
                 }
-                let in_char = input_str.as_bytes()[index_in_input] as char;
-                if in_char != fc {
-                    // mismatch
+            }
+
+            ///////////////////////////
+            // Literal % => "%%"     //
+            ///////////////////////////
+            '%' => {
+                if index_in_input >= input_str.len()
+                    || input_str.as_bytes()[index_in_input] as char != '%'
+                {
                     return ptr::null_mut();
                 }
                 index_in_input += 1;
-                continue;
             }
 
-            // If we see '%', read the next character (format specifier)
-            let Some(spec) = fmt_chars.next() else {
-                // format string ended abruptly after '%'
-                return ptr::null_mut();
-            };
+            ///////////////////////////
+            // Day of Month: %d / %e //
+            ///////////////////////////
+            'd' | 'e' => {
+                // parse a 2-digit day (with or without leading zero)
+                let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
+                    Some(v) => v,
+                    None => return ptr::null_mut(),
+                };
+                (*tm).tm_mday = val as c_int;
+                index_in_input += len;
+            }
 
-            // POSIX says `%E` or `%O` are "modified" specifiers for locale.
-            // We'll skip them if they appear (like strftime does) and read the next char.
-            let final_spec = if spec == 'E' || spec == 'O' {
-                match fmt_chars.next() {
-                    Some(ch) => ch,
+            ///////////////////////////
+            // Month: %m             //
+            ///////////////////////////
+            'm' => {
+                // parse a 2-digit month
+                let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
+                    Some(v) => v,
                     None => return ptr::null_mut(),
+                };
+                // tm_mon is 0-based (0 = Jan, 1 = Feb,...)
+                (*tm).tm_mon = (val as c_int) - 1;
+                if (*tm).tm_mon < 0 || (*tm).tm_mon > 11 {
+                    return ptr::null_mut();
                 }
-            } else {
-                spec
-            };
-
-            // Handle known specifiers
-            match final_spec {
-                ///////////////////////////
-                // Whitespace: %n or %t //
-                ///////////////////////////
-                'n' | 't' => {
-                    // Skip over any whitespace in the input
-                    while index_in_input < input_str.len()
-                        && input_str.as_bytes()[index_in_input].is_ascii_whitespace()
-                    {
-                        index_in_input += 1;
-                    }
-                }
+                index_in_input += len;
+            }
 
-                ///////////////////////////
-                // Literal % => "%%"    //
-                ///////////////////////////
-                '%' => {
-                    if index_in_input >= input_str.len()
-                        || input_str.as_bytes()[index_in_input] as char != '%'
-                    {
-                        return ptr::null_mut();
-                    }
-                    index_in_input += 1;
-                }
+            //////////////////////////////
+            // Year without century: %y //
+            //////////////////////////////
+            'y' => {
+                // parse a 2-digit year
+                let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
+                    Some(v) => v,
+                    None => return ptr::null_mut(),
+                };
+                // According to POSIX, %y in strptime is [00,99], and the "year" is 1900..1999 for [00..99],
+                // but the standard says: "values in [69..99] refer to 1969..1999, [00..68] => 2000..2068"
+                let fullyear = if val >= 69 { val + 1900 } else { val + 2000 };
+                (*tm).tm_year = (fullyear - 1900) as c_int;
+                index_in_input += len;
+            }
 
-                ///////////////////////////
-                // Day of Month: %d / %e //
-                ///////////////////////////
-                'd' | 'e' => {
-                    // parse a 2-digit day (with or without leading zero)
-                    let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    (*tm).tm_mday = val as c_int;
-                    index_in_input += len;
-                }
+            ///////////////////////////
+            // Year with century: %Y //
+            ///////////////////////////
+            'Y' => {
+                // parse up to 4-digit (or more) year
+                // We allow more than 4 digits if needed
+                let (val, len) = match parse_int(&input_str[index_in_input..], 4, true) {
+                    Some(v) => v,
+                    None => return ptr::null_mut(),
+                };
+                (*tm).tm_year = (val as c_int) - 1900;
+                index_in_input += len;
+            }
 
-                ///////////////////////////
-                // Month: %m            //
-                ///////////////////////////
-                'm' => {
-                    // parse a 2-digit month
-                    let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    // tm_mon is 0-based (0 = Jan, 1 = Feb,...)
-                    (*tm).tm_mon = (val as c_int) - 1;
-                    if (*tm).tm_mon < 0 || (*tm).tm_mon > 11 {
-                        return ptr::null_mut();
-                    }
-                    index_in_input += len;
+            ///////////////////////////
+            // Hour (00..23): %H     //
+            ///////////////////////////
+            'H' => {
+                let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
+                    Some(v) => v,
+                    None => return ptr::null_mut(),
+                };
+                if val > 23 {
+                    return ptr::null_mut();
                 }
+                (*tm).tm_hour = val as c_int;
+                index_in_input += len;
+            }
 
-                ///////////////////////////
-                // Year without century: %y
-                ///////////////////////////
-                'y' => {
-                    // parse a 2-digit year
-                    let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    // According to POSIX, %y in strptime is [00,99], and the "year" is 1900..1999 for [00..99],
-                    // but the standard says: "values in [69..99] refer to 1969..1999, [00..68] => 2000..2068"
-                    let fullyear = if val >= 69 { val + 1900 } else { val + 2000 };
-                    (*tm).tm_year = (fullyear - 1900) as c_int;
-                    index_in_input += len;
+            ///////////////////////////
+            // Hour (01..12): %I     //
+            ///////////////////////////
+            'I' => {
+                let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
+                    Some(v) => v,
+                    None => return ptr::null_mut(),
+                };
+                if val < 1 || val > 12 {
+                    return ptr::null_mut();
                 }
+                (*tm).tm_hour = val as c_int;
+                // We’ll interpret AM/PM with %p if it appears later
+                index_in_input += len;
+            }
 
-                ///////////////////////////
-                // Year with century: %Y
-                ///////////////////////////
-                'Y' => {
-                    // parse up to 4-digit (or more) year
-                    // We allow more than 4 digits if needed
-                    let (val, len) = match parse_int(&input_str[index_in_input..], 4, true) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    (*tm).tm_year = (val as c_int) - 1900;
-                    index_in_input += len;
+            ///////////////////////////
+            // Minute (00..59): %M   //
+            ///////////////////////////
+            'M' => {
+                let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
+                    Some(v) => v,
+                    None => return ptr::null_mut(),
+                };
+                if val > 59 {
+                    return ptr::null_mut();
                 }
+                (*tm).tm_min = val as c_int;
+                index_in_input += len;
+            }
 
-                ///////////////////////////
-                // Hour (00..23): %H    //
-                ///////////////////////////
-                'H' => {
-                    let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    if val > 23 {
-                        return ptr::null_mut();
-                    }
-                    (*tm).tm_hour = val as c_int;
-                    index_in_input += len;
+            ///////////////////////////
+            // Seconds (00..60): %S  //
+            ///////////////////////////
+            'S' => {
+                let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
+                    Some(v) => v,
+                    None => return ptr::null_mut(),
+                };
+                if val > 60 {
+                    return ptr::null_mut();
                 }
+                (*tm).tm_sec = val as c_int;
+                index_in_input += len;
+            }
 
-                ///////////////////////////
-                // Hour (01..12): %I    //
-                ///////////////////////////
-                'I' => {
-                    let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    if val < 1 || val > 12 {
-                        return ptr::null_mut();
+            ///////////////////////////
+            // AM/PM: %p             //
+            ///////////////////////////
+            'p' => {
+                // Parse either "AM" or "PM" (no case-sensitive)
+                // We'll read up to 2 or 3 letters from input ("AM", "PM")
+                let leftover = &input_str[index_in_input..];
+                let parsed_len = match parse_am_pm(leftover) {
+                    Some((is_pm, used)) => {
+                        if (*tm).tm_hour == 12 {
+                            // 12 AM => 00:xx, 12 PM => 12:xx
+                            (*tm).tm_hour = if is_pm { 12 } else { 0 };
+                        } else {
+                            // 1..11 AM => 1..11, 1..11 PM => 13..23
+                            if is_pm {
+                                (*tm).tm_hour += 12;
+                            }
+                        }
+                        used
                     }
-                    (*tm).tm_hour = val as c_int;
-                    // We’ll interpret AM/PM with %p if it appears later
-                    index_in_input += len;
-                }
+                    None => return ptr::null_mut(),
+                };
+                index_in_input += parsed_len;
+            }
 
-                ///////////////////////////
-                // Minute (00..59): %M  //
-                ///////////////////////////
-                'M' => {
-                    let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    if val > 59 {
-                        return ptr::null_mut();
+            ///////////////////////////
+            // Weekday Name: %a/%A   //
+            ///////////////////////////
+            'a' => {
+                // Abbreviated day name (Sun..Sat)
+                let leftover = &input_str[index_in_input..];
+                let parsed_len = match parse_weekday(leftover, true) {
+                    Some((wday, used)) => {
+                        (*tm).tm_wday = wday as c_int;
+                        used
                     }
-                    (*tm).tm_min = val as c_int;
-                    index_in_input += len;
-                }
-
-                ///////////////////////////
-                // Seconds (00..60): %S //
-                ///////////////////////////
-                'S' => {
-                    let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    if val > 60 {
-                        return ptr::null_mut();
+                    None => return ptr::null_mut(),
+                };
+                index_in_input += parsed_len;
+            }
+            'A' => {
+                // Full day name (Sunday..Saturday)
+                let leftover = &input_str[index_in_input..];
+                let parsed_len = match parse_weekday(leftover, false) {
+                    Some((wday, used)) => {
+                        (*tm).tm_wday = wday as c_int;
+                        used
                     }
-                    (*tm).tm_sec = val as c_int;
-                    index_in_input += len;
-                }
-
-                ///////////////////////////
-                // AM/PM: %p           //
-                ///////////////////////////
-                'p' => {
-                    // Parse either "AM" or "PM" (case-insensitive)
-                    // We'll read up to 2 or 3 letters from input ("AM", "PM")
-                    let leftover = &input_str[index_in_input..];
-                    let parsed_len = match parse_am_pm(leftover) {
-                        Some((is_pm, used)) => {
-                            if (*tm).tm_hour == 12 {
-                                // 12 AM => 00:xx, 12 PM => 12:xx
-                                (*tm).tm_hour = if is_pm { 12 } else { 0 };
-                            } else {
-                                // 1..11 AM => 1..11, 1..11 PM => 13..23
-                                if is_pm {
-                                    (*tm).tm_hour += 12;
-                                }
-                            }
-                            used
-                        }
-                        None => return ptr::null_mut(),
-                    };
-                    index_in_input += parsed_len;
-                }
-
-                ///////////////////////////
-                // Weekday Name: %a/%A  //
-                ///////////////////////////
-                'a' => {
-                    // Abbreviated day name (Sun..Sat)
-                    let leftover = &input_str[index_in_input..];
-                    let parsed_len = match parse_weekday(leftover, true) {
-                        Some((wday, used)) => {
-                            (*tm).tm_wday = wday as c_int;
-                            used
-                        }
-                        None => return ptr::null_mut(),
-                    };
-                    index_in_input += parsed_len;
-                }
-                'A' => {
-                    // Full day name (Sunday..Saturday)
-                    let leftover = &input_str[index_in_input..];
-                    let parsed_len = match parse_weekday(leftover, false) {
-                        Some((wday, used)) => {
-                            (*tm).tm_wday = wday as c_int;
-                            used
-                        }
-                        None => return ptr::null_mut(),
-                    };
-                    index_in_input += parsed_len;
-                }
-
-                ///////////////////////////
-                // Month Name: %b/%B/%h //
-                ///////////////////////////
-                'b' | 'h' => {
-                    // Abbreviated month name
-                    let leftover = &input_str[index_in_input..];
-                    let parsed_len = match parse_month(leftover, true) {
-                        Some((mon, used)) => {
-                            (*tm).tm_mon = mon as c_int;
-                            used
-                        }
-                        None => return ptr::null_mut(),
-                    };
-                    index_in_input += parsed_len;
-                }
-                'B' => {
-                    // Full month name
-                    let leftover = &input_str[index_in_input..];
-                    let parsed_len = match parse_month(leftover, false) {
-                        Some((mon, used)) => {
-                            (*tm).tm_mon = mon as c_int;
-                            used
-                        }
-                        None => return ptr::null_mut(),
-                    };
-                    index_in_input += parsed_len;
-                }
+                    None => return ptr::null_mut(),
+                };
+                index_in_input += parsed_len;
+            }
 
-                ///////////////////////////
-                // Day of year: %j      //
-                ///////////////////////////
-                'j' => {
-                    // parse 3-digit day of year [001..366]
-                    let (val, len) = match parse_int(&input_str[index_in_input..], 3, false) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    if val < 1 || val > 366 {
-                        return ptr::null_mut();
+            ///////////////////////////
+            // Month Name: %b/%B/%h  //
+            ///////////////////////////
+            'b' | 'h' => {
+                // Abbreviated month name
+                let leftover = &input_str[index_in_input..];
+                let parsed_len = match parse_month(leftover, true) {
+                    Some((mon, used)) => {
+                        (*tm).tm_mon = mon as c_int;
+                        used
                     }
-                    // store in tm_yday
-                    (*tm).tm_yday = (val - 1) as c_int;
-                    index_in_input += len;
-                }
-
-                ///////////////////////////
-                // Date shortcuts: %D, %F, etc.
-                ///////////////////////////
-                'D' => {
-                    // Equivalent to "%m/%d/%y"
-                    // We can do a mini strptime recursion or manually parse
-                    // For simplicity, we'll do it inline here
-                    let subfmt = "%m/%d/%y";
-                    let used = match apply_subformat(&input_str[index_in_input..], subfmt, tm) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    index_in_input += used;
-                }
-                'F' => {
-                    // Equivalent to "%Y-%m-%d"
-                    let subfmt = "%Y-%m-%d";
-                    let used = match apply_subformat(&input_str[index_in_input..], subfmt, tm) {
-                        Some(v) => v,
-                        None => return ptr::null_mut(),
-                    };
-                    index_in_input += used;
-                }
+                    None => return ptr::null_mut(),
+                };
+                index_in_input += parsed_len;
+            }
+            'B' => {
+                // Full month name
+                let leftover = &input_str[index_in_input..];
+                let parsed_len = match parse_month(leftover, false) {
+                    Some((mon, used)) => {
+                        (*tm).tm_mon = mon as c_int;
+                        used
+                    }
+                    None => return ptr::null_mut(),
+                };
+                index_in_input += parsed_len;
+            }
 
-                ///////////////////////////
-                // Not implemented: %x, %X, %c, %r, %R, %T, etc.
-                ///////////////////////////
-                // If you want to implement these, do similarly to %D / %F or parse manually
-                'x' | 'X' | 'c' | 'r' | 'R' | 'T' => {
-                    // For brevity, we skip these. You can expand similarly.
-                    // Return NULL if we don’t want to accept them:
+            ///////////////////////////
+            // Day of year: %j       //
+            ///////////////////////////
+            'j' => {
+                // parse 3-digit day of year [001..366]
+                let (val, len) = match parse_int(&input_str[index_in_input..], 3, false) {
+                    Some(v) => v,
+                    None => return ptr::null_mut(),
+                };
+                if val < 1 || val > 366 {
                     return ptr::null_mut();
                 }
+                // store in tm_yday
+                (*tm).tm_yday = (val - 1) as c_int;
+                index_in_input += len;
+            }
 
-                ///////////////////////////
-                // Timezone: %Z or %z
-                ///////////////////////////
-                'Z' | 'z' => {
-                    // Full/abbrev time zone name or numeric offset
-                    // Implementation omitted. Real support is quite complicated.
-                    return ptr::null_mut();
-                }
+            //////////////////////////////////
+            // Date shortcuts: %D, %F, etc. //
+            //////////////////////////////////
+            'D' => {
+                // Equivalent to "%m/%d/%y"
+                // We can do a mini strptime recursion or manually parse
+                // For simplicity, we'll do it inline here
+                let subfmt = "%m/%d/%y";
+                let used = match apply_subformat(&input_str[index_in_input..], subfmt, tm) {
+                    Some(v) => v,
+                    None => return ptr::null_mut(),
+                };
+                index_in_input += used;
+            }
+            'F' => {
+                // Equivalent to "%Y-%m-%d"
+                let subfmt = "%Y-%m-%d";
+                let used = match apply_subformat(&input_str[index_in_input..], subfmt, tm) {
+                    Some(v) => v,
+                    None => return ptr::null_mut(),
+                };
+                index_in_input += used;
+            }
 
-                //////////
-                // else //
-                //////////
-                _ => {
-                    // We do not recognize this specifier
-                    return ptr::null_mut();
-                }
+            //////////////////////////////////////////////////////////
+            // TODO : not implemented: %x, %X, %c, %r, %R, %T, etc. //
+            //////////////////////////////////////////////////////////
+            // Hint : if you want to implement these, do similarly to %D / %F (or parse manually)
+            'x' | 'X' | 'c' | 'r' | 'R' | 'T' => {
+                // Return NULL if we don’t want to accept them :
+                return ptr::null_mut();
             }
-        }
 
-        // If we got here, parsing was successful. Return pointer to the
-        // next unparsed character in `buf`.
-        let ret_ptr = buf.add(index_in_input);
-        ret_ptr as *mut c_char
+            ///////////////////////////
+            // Timezone: %Z or %z    //
+            ///////////////////////////
+            'Z' | 'z' => {
+                // Full/abbrev time zone name or numeric offset
+                // Implementation omitted. Real support is quite complicated.
+                return ptr::null_mut();
+            }
+
+            //////////
+            // else //
+            //////////
+            _ => {
+                // We do not recognize this specifier
+                return ptr::null_mut();
+            }
+        }
     }
+
+    // If we got here, parsing was successful. Return pointer to the
+    // next unparsed character in `buf`.
+    let ret_ptr = buf.add(index_in_input);
+    ret_ptr as *mut c_char
 }
 
 // -----------------------
 // Helper / Parsing Logic
 // -----------------------
 
-/// Convert a C char pointer to a Rust &str (assuming it's valid UTF-8).
-/// Returns an empty string if invalid.
-unsafe fn cstr_to_str<'a>(ptr: *const c_char) -> &'a str {
-    if ptr.is_null() {
-        return "";
-    }
-    let len = strlen(ptr);
-    let bytes = slice::from_raw_parts(ptr as *const u8, len);
-    str::from_utf8(bytes).unwrap_or("")
-}
-
-/// Minimal strlen for C-strings
-unsafe fn strlen(mut ptr: *const c_char) -> usize {
-    let mut count = 0;
-    while !ptr.is_null() && *ptr != 0 {
-        ptr = ptr.add(1);
-        count += 1;
-    }
-    count
-}
-
 /// Parse an integer from the beginning of `input_str`.
 ///
 /// - `width` is the maximum number of digits to parse
 /// - `allow_variable_width` indicates if we can parse fewer digits
 ///   (e.g., `%Y` can have more than 4 digits, but also might parse "2023" or "12345").
-fn parse_int(input_str: &str, width: usize, allow_variable_width: bool) -> Option<(i32, usize)> {
+fn parse_int(input: &str, width: usize, allow_variable: bool) -> Option<(i32, usize)> {
+    let mut val = 0i32;
+    let mut chars = input.chars();
     let mut count = 0;
-    let mut value: i32 = 0;
-    let chars: Vec<char> = input_str.chars().collect();
 
-    for c in chars.iter() {
+    while let Some(c) = chars.next() {
         if !c.is_ascii_digit() {
             break;
         }
-        value = value * 10 + (*c as u8 as i32 - '0' as i32);
+
+        // Check for integer overflow
+        val = val.checked_mul(10)?.checked_add((c as u8 - b'0') as i32)?;
+
         count += 1;
-        if count == width && !allow_variable_width {
+        if count == width && !allow_variable {
             break;
         }
     }
+
     if count == 0 {
-        return None; // no digits found
+        None
+    } else {
+        Some((val, count))
     }
-    Some((value, count))
 }
 
 /// Handle AM/PM. Returns (is_pm, length_consumed).
@@ -468,8 +482,8 @@ fn parse_am_pm(s: &str) -> Option<(bool, usize)> {
 }
 
 /// Parse a weekday name from `s`.
-/// - If `abbrev == true`, match short forms: "Sun".."Sat"
-/// - Otherwise, match "Sunday".."Saturday"
+/// - if `abbrev == true`, match short forms: "Mont".."Sun"
+/// - otherwise, match "Monday".."Sunday"
 /// Return (weekday_index, length_consumed).
 fn parse_weekday(s: &str, abbrev: bool) -> Option<(usize, usize)> {
     let list = if abbrev { &SHORT_DAYS } else { &LONG_DAYS };
@@ -499,8 +513,7 @@ fn parse_month(s: &str, abbrev: bool) -> Option<(usize, usize)> {
 /// Return how many characters of `input` were consumed or None on error.
 unsafe fn apply_subformat(input: &str, subfmt: &str, tm: *mut tm) -> Option<usize> {
     // We'll do a temporary strptime call on a substring.
-    // Then we see how many chars it consumed.
-    // If that call fails, we return None.
+    // Then we see how many chars it consumed. If that call fails, we return None.
     // Otherwise, we return the count.
 
     // Convert `input` to a null-terminated buffer temporarily
-- 
GitLab