time: most of <time.h>
This commit is contained in:
@@ -10,6 +10,7 @@ crate-type = ["staticlib"]
|
||||
yggdrasil-rt = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-rt.git" }
|
||||
libyalloc = { git = "https://git.alnyan.me/yggdrasil/libyalloc.git" }
|
||||
bitflags = { version = "2.4.1" }
|
||||
chrono = { version = "0.4.31", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = { git = "https://git.alnyan.me/yggdrasil/cbindgen.git", branch = "master" }
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ MAYBE pthread.h - threads
|
||||
----- sched.h - execution scheduling
|
||||
----- search.h - search tables
|
||||
----- semaphore.h - semaphores
|
||||
----- setjmp.h - stack environment declarations
|
||||
MOSTLY setjmp.h - stack environment declarations TODO signal?
|
||||
----- signal.h - signals
|
||||
MAYBE spawn.h - spawn (ADVANCED REALTIME)
|
||||
+++++ stdarg.h - handle variable argument list
|
||||
|
||||
@@ -24,8 +24,15 @@ pub type fsfilcnt_t = u64;
|
||||
pub type ino_t = u64;
|
||||
|
||||
pub type clock_t = u64;
|
||||
pub type time_t = u64;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct time_t(pub u64);
|
||||
|
||||
pub type off_t = i64;
|
||||
|
||||
pub type suseconds_t = c_ulong;
|
||||
|
||||
impl time_t {
|
||||
pub const INVALID: Self = Self(u64::MAX);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ style = "Tag"
|
||||
sys_includes = [
|
||||
"stddef.h",
|
||||
"stdint.h",
|
||||
"locale.h",
|
||||
"sys/types.h",
|
||||
"bits/time.h"
|
||||
]
|
||||
@@ -15,4 +16,4 @@ usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
||||
|
||||
[export]
|
||||
include = ["timespec"]
|
||||
include = ["timespec", "tm", "itimerspec"]
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
use core::{cmp::Ordering, mem::MaybeUninit, ptr::null_mut};
|
||||
|
||||
use chrono::Utc;
|
||||
|
||||
use crate::header::{sys_types::time_t, time::tm};
|
||||
|
||||
use super::{get_timezone, CDateTime};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn gmtime(tp: *const time_t) -> *mut tm {
|
||||
static mut BUFFER: MaybeUninit<tm> = MaybeUninit::uninit();
|
||||
gmtime_r(tp, BUFFER.as_mut_ptr())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn gmtime_r(tp: *const time_t, timep: *mut tm) -> *mut tm {
|
||||
let tp = tp.as_ref().unwrap();
|
||||
let timep = timep.as_mut().unwrap();
|
||||
|
||||
match tp.to_datetime(Utc) {
|
||||
Some(t) => {
|
||||
*timep = t.into();
|
||||
timep
|
||||
}
|
||||
None => null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn localtime(tp: *const time_t) -> *mut tm {
|
||||
static mut BUFFER: MaybeUninit<tm> = MaybeUninit::uninit();
|
||||
localtime_r(tp, BUFFER.as_mut_ptr())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn localtime_r(tp: *const time_t, timep: *mut tm) -> *mut tm {
|
||||
let tp = tp.as_ref().unwrap();
|
||||
let timep = timep.as_mut().unwrap();
|
||||
|
||||
let tz = get_timezone();
|
||||
match tp.to_datetime(tz) {
|
||||
Some(t) => {
|
||||
*timep = t.into();
|
||||
timep
|
||||
}
|
||||
None => null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn mktime(timep: *const tm) -> time_t {
|
||||
let timep = timep.as_ref().unwrap();
|
||||
let is_dst = match timep.tm_isdst.cmp(&0) {
|
||||
Ordering::Less => todo!(),
|
||||
Ordering::Equal => false,
|
||||
Ordering::Greater => true,
|
||||
};
|
||||
|
||||
let tz = get_timezone();
|
||||
match timep.to_datetime(tz) {
|
||||
Some(t) => time_t(t.timestamp() as _),
|
||||
None => time_t::INVALID,
|
||||
}
|
||||
}
|
||||
+119
-1
@@ -1,7 +1,29 @@
|
||||
use core::ffi::c_long;
|
||||
use core::{
|
||||
ffi::{c_char, c_int, c_long},
|
||||
ptr::null_mut,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc};
|
||||
|
||||
use super::sys_types::time_t;
|
||||
|
||||
mod convert;
|
||||
mod string;
|
||||
mod timer;
|
||||
mod utility;
|
||||
|
||||
/*
|
||||
TODO
|
||||
int timer_create(clockid_t, struct sigevent *restrict,
|
||||
timer_t *restrict);
|
||||
int timer_delete(timer_t);
|
||||
int timer_getoverrun(timer_t);
|
||||
int timer_gettime(timer_t, struct itimerspec *);
|
||||
int timer_settime(timer_t, int, const struct itimerspec *restrict,
|
||||
struct itimerspec *restrict);
|
||||
*/
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct timespec {
|
||||
@@ -9,4 +31,100 @@ pub struct timespec {
|
||||
pub tv_nsec: c_long,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct itimerspec {
|
||||
pub it_interval: timespec,
|
||||
pub it_value: timespec,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct tm {
|
||||
pub tm_sec: c_int,
|
||||
pub tm_min: c_int,
|
||||
pub tm_hour: c_int,
|
||||
pub tm_mday: c_int,
|
||||
pub tm_mon: c_int,
|
||||
pub tm_year: c_int,
|
||||
pub tm_wday: c_int,
|
||||
pub tm_yday: c_int,
|
||||
pub tm_isdst: c_int,
|
||||
}
|
||||
|
||||
pub type __ygg_timespec_t = timespec;
|
||||
|
||||
#[no_mangle]
|
||||
pub static mut daylight: c_int = 0;
|
||||
#[no_mangle]
|
||||
pub static mut timezone: c_long = 0;
|
||||
#[no_mangle]
|
||||
pub static mut tzname: *mut *mut c_char = null_mut();
|
||||
|
||||
pub trait CDateTime {
|
||||
fn to_naive_datetime(&self) -> Option<NaiveDateTime>;
|
||||
|
||||
fn to_datetime<Tz: TimeZone>(&self, tz: Tz) -> Option<DateTime<Tz>> {
|
||||
self.to_naive_datetime()?.and_local_timezone(tz).latest()
|
||||
}
|
||||
}
|
||||
|
||||
impl tm {
|
||||
pub fn naive_date(&self) -> Option<NaiveDate> {
|
||||
Some(NaiveDate::from_ymd(
|
||||
self.tm_year + 1900,
|
||||
u32::try_from(self.tm_mon).ok()? + 1,
|
||||
u32::try_from(self.tm_mday).ok()?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn naive_time(&self) -> Option<NaiveTime> {
|
||||
NaiveTime::from_hms_opt(
|
||||
u32::try_from(self.tm_hour).ok()?,
|
||||
u32::try_from(self.tm_min).ok()?,
|
||||
u32::try_from(self.tm_sec).ok()?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CDateTime for tm {
|
||||
fn to_naive_datetime(&self) -> Option<NaiveDateTime> {
|
||||
let date = self.naive_date()?;
|
||||
let time = self.naive_time()?;
|
||||
|
||||
Some(NaiveDateTime::new(date, time))
|
||||
}
|
||||
}
|
||||
|
||||
impl CDateTime for time_t {
|
||||
fn to_naive_datetime(&self) -> Option<NaiveDateTime> {
|
||||
NaiveDateTime::from_timestamp_opt(self.0.try_into().ok()?, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Datelike + Timelike> From<T> for tm {
|
||||
fn from(value: T) -> Self {
|
||||
Self {
|
||||
tm_sec: value.second() as _,
|
||||
tm_min: value.minute() as _,
|
||||
tm_hour: value.hour() as _,
|
||||
tm_mday: value.day() as _,
|
||||
tm_mon: value.month0() as _,
|
||||
tm_year: value.year() - 1900,
|
||||
tm_wday: value.weekday().num_days_from_sunday() as _,
|
||||
tm_yday: value.ordinal0() as _,
|
||||
tm_isdst: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<timespec> for Duration {
|
||||
fn from(value: timespec) -> Self {
|
||||
Self::new(value.tv_sec.0, value.tv_nsec.try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_timezone() -> impl TimeZone {
|
||||
// TODO get it from C
|
||||
Utc
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
use core::{ffi::c_char, ptr::null_mut};
|
||||
|
||||
use chrono::{Datelike, TimeZone, Timelike, Utc};
|
||||
|
||||
use crate::{
|
||||
header::{locale::locale_t, sys_types::time_t},
|
||||
util::CStringWriter,
|
||||
};
|
||||
|
||||
use super::{get_timezone, tm, CDateTime};
|
||||
|
||||
unsafe fn fmt_time<Tz: TimeZone, T: CDateTime>(
|
||||
t: &T,
|
||||
tz: Tz,
|
||||
buffer: *mut c_char,
|
||||
) -> Option<*mut c_char> {
|
||||
const MONTHS: &[&str] = &[
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
||||
];
|
||||
const WDAYS: &[&str] = &["Sun", "Mon", "Wed", "Thu", "Fri", "Sat"];
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
let dt = t.to_datetime(tz)?;
|
||||
let mut writer = CStringWriter::from_ptr(buffer, usize::MAX)?;
|
||||
|
||||
writeln!(
|
||||
writer,
|
||||
"{} {} {:02} {:02}:{:02}:{:02} {:04}",
|
||||
WDAYS[dt.weekday().num_days_from_sunday() as usize],
|
||||
MONTHS[dt.month0() as usize],
|
||||
dt.day(),
|
||||
dt.hour(),
|
||||
dt.minute(),
|
||||
dt.second(),
|
||||
dt.year()
|
||||
)
|
||||
.ok()?;
|
||||
|
||||
writer.finish()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn asctime(timep: *const tm) -> *mut c_char {
|
||||
static mut BUFFER: [c_char; 32] = [0; 32];
|
||||
asctime_r(timep, BUFFER.as_mut_ptr())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn asctime_r(timep: *const tm, buffer: *mut c_char) -> *mut c_char {
|
||||
let timep = timep.as_ref().unwrap();
|
||||
|
||||
// Utc, already accounts for timezone
|
||||
fmt_time(timep, Utc, buffer).unwrap_or(null_mut())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ctime(tp: *const time_t) -> *mut c_char {
|
||||
static mut BUFFER: [c_char; 32] = [0; 32];
|
||||
ctime_r(tp, BUFFER.as_mut_ptr())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ctime_r(tp: *const time_t, buffer: *mut c_char) -> *mut c_char {
|
||||
let tp = tp.as_ref().unwrap();
|
||||
let tz = get_timezone();
|
||||
|
||||
fmt_time(tp, tz, buffer).unwrap_or(null_mut())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getdate(string: *const c_char) -> *mut tm {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getdate_r(string: *const c_char, timep: *mut tm) -> *mut tm {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn strftime(
|
||||
dst: *mut c_char,
|
||||
len: usize,
|
||||
format: *const c_char,
|
||||
timep: *const tm,
|
||||
) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn strftime_l(
|
||||
dst: *mut c_char,
|
||||
len: usize,
|
||||
format: *const c_char,
|
||||
timep: *const tm,
|
||||
locale: locale_t,
|
||||
) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn strptime(
|
||||
src: *const c_char,
|
||||
format: *const c_char,
|
||||
timep: *mut tm,
|
||||
) -> *mut c_char {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use core::ffi::c_int;
|
||||
|
||||
use crate::header::sys_types::{clock_t, clockid_t, pid_t};
|
||||
|
||||
use super::timespec;
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn clock() -> clock_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn clock_getcpuclockid(pid: pid_t, clock_id_ptr: *mut clockid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn clock_getres(clock_id: clockid_t, ts: *mut timespec) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn clock_gettime(clock_id: clockid_t, ts: *mut timespec) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn clock_settime(clock_id: clockid_t, ts: *const timespec) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn clock_nanosleep(
|
||||
clock_id: clockid_t,
|
||||
flags: c_int,
|
||||
rqtp: *const timespec,
|
||||
rmtp: *mut timespec,
|
||||
) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// TODO Timer something
|
||||
@@ -0,0 +1,47 @@
|
||||
use core::{
|
||||
ffi::{c_double, c_int},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{header::sys_types::time_t, util};
|
||||
|
||||
use super::timespec;
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int {
|
||||
let rqtp = rqtp.as_ref().unwrap();
|
||||
|
||||
let amount = Duration::from(*rqtp);
|
||||
|
||||
match util::nanosleep(amount, None) {
|
||||
Ok(()) => {
|
||||
if let Some(rmtp) = rmtp.as_mut() {
|
||||
// Zero
|
||||
*rmtp = timespec::default();
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
Err(_) => {
|
||||
if let Some(rmtp) = rmtp.as_mut() {
|
||||
todo!();
|
||||
}
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn time(tp: *mut time_t) -> time_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn tzset() {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn difftime(a: time_t, b: time_t) -> c_double {
|
||||
todo!()
|
||||
}
|
||||
+58
-2
@@ -1,13 +1,26 @@
|
||||
use core::marker::PhantomData;
|
||||
use core::{
|
||||
ffi::c_char,
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
ptr::{null_mut, NonNull},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use yggdrasil_rt::path::Path;
|
||||
use yggdrasil_rt::{path::Path, sys as syscall};
|
||||
|
||||
use crate::{
|
||||
error::{EResult, OptionExt},
|
||||
header::errno::Errno,
|
||||
path::{PathBuf, PathExt},
|
||||
process::getenv,
|
||||
};
|
||||
|
||||
pub struct CStringWriter {
|
||||
ptr: NonNull<c_char>,
|
||||
pos: usize,
|
||||
limit: usize,
|
||||
}
|
||||
|
||||
pub trait Nullable {
|
||||
fn is_null(&self) -> bool;
|
||||
fn ensure(&self);
|
||||
@@ -64,6 +77,40 @@ impl<'a, T: Nullable + 'a> Iterator for NullTerminated<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl CStringWriter {
|
||||
pub unsafe fn from_ptr(ptr: *mut c_char, limit: usize) -> Option<Self> {
|
||||
let ptr = NonNull::new(ptr)?;
|
||||
Some(Self { ptr, pos: 0, limit })
|
||||
}
|
||||
|
||||
pub fn finish(self) -> Option<*mut c_char> {
|
||||
if self.pos != self.limit {
|
||||
unsafe {
|
||||
self.ptr.as_ptr().add(self.pos).write(0);
|
||||
}
|
||||
Some(self.ptr.as_ptr())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for CStringWriter {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
if self.pos + s.len() < self.limit {
|
||||
let dst = unsafe {
|
||||
core::slice::from_raw_parts_mut(self.ptr.as_ptr().add(self.pos) as *mut u8, s.len())
|
||||
};
|
||||
dst.copy_from_slice(s.as_bytes());
|
||||
self.pos += s.len();
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_binary<P: AsRef<Path>>(name: P) -> EResult<PathBuf> {
|
||||
let name = name.as_ref();
|
||||
if name.is_absolute() {
|
||||
@@ -80,3 +127,12 @@ pub fn resolve_binary<P: AsRef<Path>>(name: P) -> EResult<PathBuf> {
|
||||
|
||||
EResult::Err(yggdrasil_rt::Error::DoesNotExist)
|
||||
}
|
||||
|
||||
// TODO EINTR for interrupted sleeps
|
||||
pub fn nanosleep(amount: Duration, remaining: Option<&mut Duration>) -> Result<(), Errno> {
|
||||
if remaining.is_some() {
|
||||
todo!();
|
||||
}
|
||||
unsafe { syscall::nanosleep(amount) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user