Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • ashton/relibc
  • vincent/relibc
  • 4lDO2/relibc
  • boomshroom/relibc
  • njskalski/relibc
  • bjorn3/relibc
  • microcolonel/relibc
  • gmacd/relibc
  • feliwir/relibc
  • devnexen/relibc
  • jamesgraves/relibc
  • oddcoder/relibc
  • andar1an/relibc
  • gugz0r/relibc
  • matijaskala/relibc
  • zen3ger/relibc
  • josh/relibc
  • redox-os/relibc
  • Majoneza/relibc
  • enygmator/relibc
  • JustAnotherDev/relibc
  • doriancodes/relibc
  • adamantinum/relibc
  • wiredtv/relibc
  • stratact/relibc
  • Ramla-I/relibc
  • martin/relibc
  • bitstr0m/relibc
  • henritel/relibc
  • smckay/relibc
  • xTibor/relibc
  • devajithvs/relibc
  • andypython/relibc
  • t-nil/relibc
  • DataTriny/relibc
  • ids1024/relibc
  • SteveLauC/relibc
  • dlrobertson/relibc
  • AgostonSzepessy/relibc
  • TheDarkula/relibc
  • willnode/relibc
  • bamontan/relibc
  • redoxeon/relibc
  • ayf/relibc
  • heghe/relibc
  • Ivan/relibc
  • hasheddan/relibc
  • dahc/relibc
  • auwardoctor/relibc
  • kodicraft/relibc
  • arthurpaulino/relibc
  • jasonhansel/relibc
  • bpisch/relibc
  • kel/relibc
  • GrayJack/relibc
  • darley/relibc
  • sahitpj/relibc
  • plimkilde/relibc
  • BjornTheProgrammer/relibc
  • defra/relibc
  • Schyrsivochter/relibc
  • ebalalic/relibc
  • adchacon/relibc
  • aaronjanse/relibc
  • josh_williams/relibc
  • 8tab/relibc
  • nicoan/relibc
  • athei/relibc
  • carrot93/relibc
  • RA_GM1/relibc
  • zhaozhao/relibc
  • JCake/relibc
  • KGrewal1/relibc
  • emturner/relibc
  • LuigiPiucco/relibc
  • bfrascher/relibc
  • starsheriff/relibc
  • kcired/relibc
  • jamespcfrancis/relibc
  • neallred/relibc
  • omar-mohamed-khallaf/relibc
  • jD91mZM2/relibc
  • rw_van/relibc
  • Skallwar/relibc
  • matt-vdv/relibc
  • mati865/relibc
  • SoyaOhnishi/relibc
  • ArniDagur/relibc
  • tlam/relibc
  • glongo/relibc
  • kamirr/relibc
  • abdullah/relibc
  • saeedtabrizi/relibc
  • sajattack/relibc
  • lmiskiew/relibc
  • seanpk/relibc
  • MaikuZ/relibc
  • jamadazi/relibc
  • coolreader18/relibc
  • wt/relibc
  • lebensterben/relibc
  • uuuvn/relibc
  • vadorovsky/relibc
  • raffaeleragni/relibc
  • freewilll/relibc
  • LLeny/relibc
  • alfredoyang/relibc
  • batonius/relibc
  • TornaxO7/relibc
  • Arcterus/relibc
  • Tommoa/relibc
  • samuela/relibc
  • mindriot101/relibc
  • lygstate/relibc
114 results
Show changes
Showing
with 2593 additions and 594 deletions
...@@ -34,7 +34,7 @@ unsafe fn scatter(iovs: &[iovec], vec: Vec<u8>) { ...@@ -34,7 +34,7 @@ unsafe fn scatter(iovs: &[iovec], vec: Vec<u8>) {
let mut i = 0; let mut i = 0;
for iov in iovs.iter() { for iov in iovs.iter() {
let slice = iov.to_slice(); let slice = iov.to_slice();
slice.copy_from_slice(&vec[i..]); slice.copy_from_slice(&vec[i..][..slice.len()]);
i += slice.len(); i += slice.len();
} }
} }
...@@ -42,7 +42,7 @@ unsafe fn scatter(iovs: &[iovec], vec: Vec<u8>) { ...@@ -42,7 +42,7 @@ unsafe fn scatter(iovs: &[iovec], vec: Vec<u8>) {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t { pub unsafe extern "C" fn readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t {
if iovcnt < 0 || iovcnt > IOV_MAX { if iovcnt < 0 || iovcnt > IOV_MAX {
platform::errno = errno::EINVAL; platform::ERRNO.set(errno::EINVAL);
return -1; return -1;
} }
...@@ -59,7 +59,7 @@ pub unsafe extern "C" fn readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> s ...@@ -59,7 +59,7 @@ pub unsafe extern "C" fn readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> s
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn writev(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t { pub unsafe extern "C" fn writev(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t {
if iovcnt < 0 || iovcnt > IOV_MAX { if iovcnt < 0 || iovcnt > IOV_MAX {
platform::errno = errno::EINVAL; platform::ERRNO.set(errno::EINVAL);
return -1; return -1;
} }
......
//! `sys/un.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html>.
use crate::{header::sys_socket::sa_family_t, platform::types::*}; use crate::{header::sys_socket::sa_family_t, platform::types::*};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html>.
#[repr(C)] #[repr(C)]
pub struct sockaddr_un { pub struct sockaddr_un {
sun_family: sa_family_t, pub sun_family: sa_family_t,
sun_path: [c_char; 108], pub sun_path: [c_char; 108],
}
impl sockaddr_un {
pub fn path_offset(&self) -> usize {
let base = self as *const _ as usize;
let path = &self.sun_path as *const _ as usize;
trace!("base: {:#X}, path: {:#X}", base, path);
path - base
}
} }
//! sys/utsname implementation, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysutsname.h.html //! sys/utsname implementation, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysutsname.h.html
use crate::platform::{types::*, Pal, Sys}; use crate::{
error::ResultExt,
platform::{types::*, Pal, Sys},
};
pub const UTSLENGTH: usize = 65; pub const UTSLENGTH: usize = 65;
...@@ -16,5 +19,5 @@ pub struct utsname { ...@@ -16,5 +19,5 @@ pub struct utsname {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn uname(uts: *mut utsname) -> c_int { pub unsafe extern "C" fn uname(uts: *mut utsname) -> c_int {
Sys::uname(uts) Sys::uname(uts).map(|()| 0).or_minus_one_errno()
} }
//! sys/wait.h implementation for Redox, following //! sys/wait.h implementation for Redox, following
//! http://pubs.opengroup.org/onlinepubs/7908799/xsh/syswait.h.html //! http://pubs.opengroup.org/onlinepubs/7908799/xsh/syswait.h.html
use crate::error::ResultExt;
//use header::sys_resource::rusage; //use header::sys_resource::rusage;
use crate::platform::{types::*, Pal, Sys}; use crate::platform::{types::*, Pal, Sys};
...@@ -47,5 +48,5 @@ pub unsafe extern "C" fn wait(stat_loc: *mut c_int) -> pid_t { ...@@ -47,5 +48,5 @@ pub unsafe extern "C" fn wait(stat_loc: *mut c_int) -> pid_t {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t { pub unsafe extern "C" fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t {
Sys::waitpid(pid, stat_loc, options) Sys::waitpid(pid, stat_loc, options).or_minus_one_errno()
} }
sys_includes = ["sys/types.h"]
include_guard = "_TAR_H"
trailer = "#include <bits/tar.h>"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
\ No newline at end of file
//! tar.h implementation for Redox, following POSIX.1-1990 specification
//! and https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/tar.h.html
use core::slice;
/// Block size for tar archives (512 bytes).
pub const BLOCKSIZE: usize = 512;
/// Default record size for tar archives (10KB, consisting of 20 blocks).
pub const RECORDSIZE: usize = BLOCKSIZE * 20; // 10KB (default for tar archives)
/// Field lengths in tar headers
pub const NAME_SIZE: usize = 100; // File name
pub const MODE_SIZE: usize = 8; // File mode
pub const UID_SIZE: usize = 8; // Owner's numeric user ID
pub const GID_SIZE: usize = 8; // Group's numeric user ID
pub const SIZE_SIZE: usize = 12; // File size in bytes
pub const MTIME_SIZE: usize = 12; // Modification time
pub const CHKSUM_SIZE: usize = 8; // Checksum
pub const LINKNAME_SIZE: usize = 100; // Name of linked file
pub const MAGIC_SIZE: usize = 6; // Magic string size
pub const VERSION_SIZE: usize = 2; // Version string size
pub const UNAME_SIZE: usize = 32; // Owner user name
pub const GNAME_SIZE: usize = 32; // Owner group name
pub const DEVMAJOR_SIZE: usize = 8; // Major device number
pub const DEVMINOR_SIZE: usize = 8; // Minor device number
pub const PREFIX_SIZE: usize = 155; // Prefix for file name
pub const HEADER_SIZE: usize = 512; // Total header size
/// Bits used in the mode field - value in octal
pub const TSUID: u16 = 0o4000; // Set user ID on execution
pub const TSGID: u16 = 0o2000; // Set group ID on execution
pub const TSVTX: u16 = 0o1000; // Sticky bit
pub const TUREAD: u16 = 0o0400; // Read permission, owner
pub const TUWRITE: u16 = 0o0200; // Write permission, owner
pub const TUEXEC: u16 = 0o0100; // Execute/search permission, owner
pub const TGREAD: u16 = 0o0040; // Read permission, group
pub const TGWRITE: u16 = 0o0020; // Write permission, group
pub const TGEXEC: u16 = 0o0010; // Execute/search permission, group
pub const TOREAD: u16 = 0o0004; // Read permission, others
pub const TOWRITE: u16 = 0o0002; // Write permission, others
pub const TOEXEC: u16 = 0o0001; // Execute/search permission, others
/// Values used in typeflag field
pub const REGTYPE: u8 = b'0'; // Regular file
pub const AREGTYPE: u8 = b'\0'; // Regular file (old format)
pub const LNKTYPE: u8 = b'1'; // Link
pub const SYMTYPE: u8 = b'2'; // Symbolic link
pub const CHRTYPE: u8 = b'3'; // Character special
pub const BLKTYPE: u8 = b'4'; // Block special
pub const DIRTYPE: u8 = b'5'; // Directory
pub const FIFOTYPE: u8 = b'6'; // FIFO special
pub const CONTTYPE: u8 = b'7'; // Contiguous file
/// tar format magic and version
pub const TMAGIC: &str = "ustar"; // Magic string : ustar and a null
pub const TMAGLEN: usize = 6; // Length of the magic string
pub const TVERSION: &str = "00"; // Version string
pub const TVERSLEN: usize = 2; // Length of the version string
/// Reserved for future standards
pub const XHDRTYPE: u8 = b'x'; // Extended header referring to the next file in the archive
pub const XGLTYPE: u8 = b'g'; // Global extended header
/// Reserved values for GNU tar extensions
// pub const GNUTYPE_DUMPDIR: u8 = b'D'; // Directory dump
// pub const GNUTYPE_MULTIVOL: u8 = b'M'; // Multi-volume file
// pub const GNUTYPE_LONGNAME: u8 = b'L'; // Long file name
// pub const GNUTYPE_LONGLINK: u8 = b'K'; // Long link name
// pub const GNUTYPE_SPARSE: u8 = b'S'; // Sparse file
/// Represents a tar archive header following the POSIX ustar format.
///
/// The header contains metadata about a file in a tar archive, including
/// its name, size, permissions, and other attributes. All text fields are
/// null-terminated.
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct TarHeader {
// Byte offset - usage
pub name: [u8; NAME_SIZE], // 0 - File name
pub mode: [u8; MODE_SIZE], // 100 - Permissions
pub uid: [u8; UID_SIZE], // 108 - User ID
pub gid: [u8; GID_SIZE], // 116 - Group ID
pub size: [u8; SIZE_SIZE], // 124 - File size in bytes
pub mtime: [u8; MTIME_SIZE], // 136 - Modification time
pub chksum: [u8; CHKSUM_SIZE], // 148 - Header checksum
pub typeflag: u8, // 156 - File type
pub linkname: [u8; LINKNAME_SIZE], // 157 - Linked file name
pub magic: [u8; MAGIC_SIZE], // 257 - UStar magic
pub version: [u8; VERSION_SIZE], // 263 - UStar version
pub uname: [u8; UNAME_SIZE], // 265 - Owner user name
pub gname: [u8; GNAME_SIZE], // 297 - Owner group name
pub devmajor: [u8; DEVMAJOR_SIZE], // 329 - Major device number
pub devminor: [u8; DEVMINOR_SIZE], // 337 - Minor device number
pub prefix: [u8; PREFIX_SIZE], // 345 - Prefix for file name
pub padding: [u8; 12], // 500 - Padding to make 512 bytes
}
impl Default for TarHeader {
fn default() -> Self {
let mut header = Self {
name: [0; NAME_SIZE],
mode: [0; MODE_SIZE],
uid: [0; UID_SIZE],
gid: [0; GID_SIZE],
size: [0; SIZE_SIZE],
mtime: [0; MTIME_SIZE],
chksum: [0; CHKSUM_SIZE],
typeflag: AREGTYPE,
linkname: [0; LINKNAME_SIZE],
magic: [0; MAGIC_SIZE],
version: [0; VERSION_SIZE],
uname: [0; UNAME_SIZE],
gname: [0; GNAME_SIZE],
devmajor: [0; DEVMAJOR_SIZE],
devminor: [0; DEVMINOR_SIZE],
prefix: [0; PREFIX_SIZE],
padding: [0; 12],
};
// Set default magic ("ustar") and version ("00")
let magic_bytes = TMAGIC.as_bytes(); // "ustar"
header.magic[..magic_bytes.len()].copy_from_slice(magic_bytes);
// tar specification often expects "ustar\0"
if MAGIC_SIZE >= 6 && TMAGIC.len() < MAGIC_SIZE {
header.magic[TMAGIC.len()] = 0;
}
let version_bytes = TVERSION.as_bytes(); // "00"
header.version[..version_bytes.len()].copy_from_slice(version_bytes);
header
}
}
impl TarHeader {
/// Calculates the checksum of the tar header as required by the specification.
/// Before computing, the checksum field is treated as if it contained all spaces (0x20).
pub fn calculate_checksum(&self) -> usize {
let mut header_copy = *self;
header_copy.chksum.fill(b' ');
let bytes =
unsafe { slice::from_raw_parts(&header_copy as *const _ as *const u8, HEADER_SIZE) };
bytes.iter().map(|&b| b as usize).sum()
}
}
sys_includes = ["stdint.h"] sys_includes = ["stdint.h", "features.h"]
include_guard = "_TERMIOS_H" include_guard = "_RELIBC_TERMIOS_H"
language = "C" language = "C"
style = "Tag" style = "Tag"
no_includes = true no_includes = true
...@@ -7,3 +7,6 @@ cpp_compat = true ...@@ -7,3 +7,6 @@ cpp_compat = true
[enum] [enum]
prefix_with_name = true prefix_with_name = true
[export.rename]
"winsize" = "struct winsize"
/* c_cc { */
pub const VINTR: usize = 0;
pub const VQUIT: usize = 1;
pub const VERASE: usize = 2;
pub const VKILL: usize = 3;
pub const VEOF: usize = 4;
pub const VTIME: usize = 5;
pub const VMIN: usize = 6;
pub const VSWTC: usize = 7;
pub const VSTART: usize = 8;
pub const VSTOP: usize = 9;
pub const VSUSP: usize = 10;
pub const VEOL: usize = 11;
pub const VREPRINT: usize = 12;
pub const VDISCARD: usize = 13;
pub const VWERASE: usize = 14;
pub const VLNEXT: usize = 15;
pub const VEOL2: usize = 16;
pub const NCCS: usize = 32;
/* } c_cc */
/* c_iflag { */
pub const IGNBRK: usize = 0o000_001;
pub const BRKINT: usize = 0o000_002;
pub const IGNPAR: usize = 0o000_004;
pub const PARMRK: usize = 0o000_010;
pub const INPCK: usize = 0o000_020;
pub const ISTRIP: usize = 0o000_040;
pub const INLCR: usize = 0o000_100;
pub const IGNCR: usize = 0o000_200;
pub const ICRNL: usize = 0o000_400;
pub const IUCLC: usize = 0o001_000;
pub const IXON: usize = 0o002_000;
pub const IXANY: usize = 0o004_000;
pub const IXOFF: usize = 0o010_000;
pub const IMAXBEL: usize = 0o020_000;
pub const IUTF8: usize = 0o040_000;
/* } c_iflag */
/* c_oflag { */
pub const OPOST: usize = 0o000_001;
pub const OLCUC: usize = 0o000_002;
pub const ONLCR: usize = 0o000_004;
pub const OCRNL: usize = 0o000_010;
pub const ONOCR: usize = 0o000_020;
pub const ONLRET: usize = 0o000_040;
pub const OFILL: usize = 0o000_100;
pub const OFDEL: usize = 0o000_200;
pub const VTDLY: usize = 0o040_000;
pub const VT0: usize = 0o000_000;
pub const VT1: usize = 0o040_000;
/* } c_oflag */
/* c_cflag { */
pub const B0: usize = 0o000_000;
pub const B50: usize = 0o000_001;
pub const B75: usize = 0o000_002;
pub const B110: usize = 0o000_003;
pub const B134: usize = 0o000_004;
pub const B150: usize = 0o000_005;
pub const B200: usize = 0o000_006;
pub const B300: usize = 0o000_007;
pub const B600: usize = 0o000_010;
pub const B1200: usize = 0o000_011;
pub const B1800: usize = 0o000_012;
pub const B2400: usize = 0o000_013;
pub const B4800: usize = 0o000_014;
pub const B9600: usize = 0o000_015;
pub const B19200: usize = 0o000_016;
pub const B38400: usize = 0o000_017;
pub const B57600: usize = 0o010_001;
pub const B115200: usize = 0o010_002;
pub const B230400: usize = 0o010_003;
pub const B460800: usize = 0o010_004;
pub const B500000: usize = 0o010_005;
pub const B576000: usize = 0o010_006;
pub const B921600: usize = 0o010_007;
pub const B1000000: usize = 0o010_010;
pub const B1152000: usize = 0o010_011;
pub const B1500000: usize = 0o010_012;
pub const B2000000: usize = 0o010_013;
pub const B2500000: usize = 0o010_014;
pub const B3000000: usize = 0o010_015;
pub const B3500000: usize = 0o010_016;
pub const B4000000: usize = 0o010_017;
pub const CSIZE: usize = 0o000_060;
pub const CS5: usize = 0o000_000;
pub const CS6: usize = 0o000_020;
pub const CS7: usize = 0o000_040;
pub const CS8: usize = 0o000_060;
pub const CSTOPB: usize = 0o000_100;
pub const CREAD: usize = 0o000_200;
pub const PARENB: usize = 0o000_400;
pub const PARODD: usize = 0o001_000;
pub const HUPCL: usize = 0o002_000;
pub const CLOCAL: usize = 0o004_000;
/* } c_clfag */
/* c_lflag { */
pub const ISIG: usize = 0o000_001;
pub const ICANON: usize = 0o000_002;
pub const ECHO: usize = 0o000_010;
pub const ECHOE: usize = 0o000_020;
pub const ECHOK: usize = 0o000_040;
pub const ECHONL: usize = 0o000_100;
pub const NOFLSH: usize = 0o000_200;
pub const TOSTOP: usize = 0o000_400;
pub const IEXTEN: usize = 0o100_000;
/* } c_lflag */
//! termios implementation, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html //! termios implementation, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html
use crate::{ use crate::{
header::{errno, sys_ioctl}, header::{
errno,
sys_ioctl::{self, winsize},
},
platform::{self, types::*}, platform::{self, types::*},
}; };
pub use self::sys::*;
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
pub mod sys;
#[cfg(target_os = "redox")]
#[path = "redox.rs"]
pub mod sys;
pub type cc_t = u8; pub type cc_t = u8;
pub type speed_t = u32; pub type speed_t = u32;
pub type tcflag_t = u32; pub type tcflag_t = u32;
pub const NCCS: usize = 32; pub const TCOOFF: c_int = 0;
pub const TCOON: c_int = 1;
pub const VINTR: usize = 0; pub const TCIOFF: c_int = 2;
pub const VQUIT: usize = 1; pub const TCION: c_int = 3;
pub const VERASE: usize = 2;
pub const VKILL: usize = 3; pub const TCIFLUSH: c_int = 0;
pub const VEOF: usize = 4; pub const TCOFLUSH: c_int = 1;
pub const VTIME: usize = 5; pub const TCIOFLUSH: c_int = 2;
pub const VMIN: usize = 6;
pub const VSWTC: usize = 7; pub const TCSANOW: c_int = 0;
pub const VSTART: usize = 8; pub const TCSADRAIN: c_int = 1;
pub const VSTOP: usize = 9; pub const TCSAFLUSH: c_int = 2;
pub const VSUSP: usize = 10;
pub const VEOL: usize = 11; #[cfg(target_os = "linux")]
pub const VREPRINT: usize = 12; #[repr(C)]
pub const VDISCARD: usize = 13; #[derive(Default, Clone)]
pub const VWERASE: usize = 14; pub struct termios {
pub const VLNEXT: usize = 15; pub c_iflag: tcflag_t,
pub const VEOL2: usize = 16; pub c_oflag: tcflag_t,
pub c_cflag: tcflag_t,
pub const IGNBRK: usize = 0o000_001; pub c_lflag: tcflag_t,
pub const BRKINT: usize = 0o000_002; pub c_line: cc_t,
pub const IGNPAR: usize = 0o000_004; pub c_cc: [cc_t; NCCS],
pub const PARMRK: usize = 0o000_010; pub __c_ispeed: speed_t,
pub const INPCK: usize = 0o000_020; pub __c_ospeed: speed_t,
pub const ISTRIP: usize = 0o000_040; }
pub const INLCR: usize = 0o000_100;
pub const IGNCR: usize = 0o000_200;
pub const ICRNL: usize = 0o000_400;
pub const IUCLC: usize = 0o001_000;
pub const IXON: usize = 0o002_000;
pub const IXANY: usize = 0o004_000;
pub const IXOFF: usize = 0o010_000;
pub const IMAXBEL: usize = 0o020_000;
pub const IUTF8: usize = 0o040_000;
pub const OPOST: usize = 0o000_001;
pub const OLCUC: usize = 0o000_002;
pub const ONLCR: usize = 0o000_004;
pub const OCRNL: usize = 0o000_010;
pub const ONOCR: usize = 0o000_020;
pub const ONLRET: usize = 0o00_0040;
pub const OFILL: usize = 0o000_100;
pub const OFDEL: usize = 0o000_200;
pub const VTDLY: usize = 0o040_000;
pub const VT0: usize = 0o000_000;
pub const VT1: usize = 0o040_000;
pub const B0: usize = 0o000_000;
pub const B50: usize = 0o000_001;
pub const B75: usize = 0o000_002;
pub const B110: usize = 0o000_003;
pub const B134: usize = 0o000_004;
pub const B150: usize = 0o000_005;
pub const B200: usize = 0o000_006;
pub const B300: usize = 0o000_007;
pub const B600: usize = 0o000_010;
pub const B1200: usize = 0o000_011;
pub const B1800: usize = 0o000_012;
pub const B2400: usize = 0o000_013;
pub const B4800: usize = 0o000_014;
pub const B9600: usize = 0o000_015;
pub const B19200: usize = 0o000_016;
pub const B38400: usize = 0o000_017;
pub const B57600: usize = 0o010_001;
pub const B115200: usize = 0o010_002;
pub const B230400: usize = 0o010_003;
pub const B460800: usize = 0o010_004;
pub const B500000: usize = 0o010_005;
pub const B576000: usize = 0o010_006;
pub const B921600: usize = 0o010_007;
pub const B1000000: usize = 0o010_010;
pub const B1152000: usize = 0o010_011;
pub const B1500000: usize = 0o010_012;
pub const B2000000: usize = 0o010_013;
pub const B2500000: usize = 0o010_014;
pub const B3000000: usize = 0o010_015;
pub const B3500000: usize = 0o010_016;
pub const B4000000: usize = 0o010_017;
pub const CSIZE: usize = 0o000_060;
pub const CS5: usize = 0o000_000;
pub const CS6: usize = 0o000_020;
pub const CS7: usize = 0o000_040;
pub const CS8: usize = 0o000_060;
pub const CSTOPB: usize = 0o000_100;
pub const CREAD: usize = 0o000_200;
pub const PARENB: usize = 0o000_400;
pub const PARODD: usize = 0o001_000;
pub const HUPCL: usize = 0o002_000;
pub const CLOCAL: usize = 0o004_000;
pub const ISIG: usize = 0o000_001;
pub const ICANON: usize = 0o000_002;
pub const ECHO: usize = 0o000_010;
pub const ECHOE: usize = 0o000_020;
pub const ECHOK: usize = 0o000_040;
pub const ECHONL: usize = 0o000_100;
pub const NOFLSH: usize = 0o000_200;
pub const TOSTOP: usize = 0o000_400;
pub const IEXTEN: usize = 0o100_000;
pub const TCOOFF: usize = 0;
pub const TCOON: usize = 1;
pub const TCIOFF: usize = 2;
pub const TCION: usize = 3;
pub const TCIFLUSH: usize = 0;
pub const TCOFLUSH: usize = 1;
pub const TCIOFLUSH: usize = 2;
pub const TCSANOW: usize = 0;
pub const TCSADRAIN: usize = 1;
pub const TCSAFLUSH: usize = 2;
// Must match structure in redox_termios
#[cfg(target_os = "redox")]
#[repr(C)] #[repr(C)]
#[derive(Default)] #[derive(Default, Clone)]
pub struct termios { pub struct termios {
c_iflag: tcflag_t, pub c_iflag: tcflag_t,
c_oflag: tcflag_t, pub c_oflag: tcflag_t,
c_cflag: tcflag_t, pub c_cflag: tcflag_t,
c_lflag: tcflag_t, pub c_lflag: tcflag_t,
c_line: cc_t, pub c_cc: [cc_t; NCCS],
c_cc: [cc_t; NCCS],
__c_ispeed: speed_t,
__c_ospeed: speed_t,
} }
#[no_mangle] #[no_mangle]
...@@ -145,25 +67,42 @@ pub unsafe extern "C" fn tcgetattr(fd: c_int, out: *mut termios) -> c_int { ...@@ -145,25 +67,42 @@ pub unsafe extern "C" fn tcgetattr(fd: c_int, out: *mut termios) -> c_int {
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tcsetattr(fd: c_int, act: c_int, value: *mut termios) -> c_int { pub unsafe extern "C" fn tcsetattr(fd: c_int, act: c_int, value: *const termios) -> c_int {
if act < 0 || act > 2 { if act < 0 || act > 2 {
platform::errno = errno::EINVAL; platform::ERRNO.set(errno::EINVAL);
return -1; return -1;
} }
// This is safe because ioctl shouldn't modify the value // This is safe because ioctl shouldn't modify the value
sys_ioctl::ioctl(fd, sys_ioctl::TCSETS + act as c_ulong, value as *mut c_void) sys_ioctl::ioctl(fd, sys_ioctl::TCSETS + act as c_ulong, value as *mut c_void)
} }
#[cfg(target_os = "linux")]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn cfgetispeed(termios_p: *const termios) -> speed_t { pub unsafe extern "C" fn cfgetispeed(termios_p: *const termios) -> speed_t {
(*termios_p).__c_ispeed (*termios_p).__c_ispeed
} }
#[cfg(target_os = "redox")]
#[no_mangle]
pub unsafe extern "C" fn cfgetispeed(termios_p: *const termios) -> speed_t {
//TODO
0
}
#[cfg(target_os = "linux")]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn cfgetospeed(termios_p: *const termios) -> speed_t { pub unsafe extern "C" fn cfgetospeed(termios_p: *const termios) -> speed_t {
(*termios_p).__c_ospeed (*termios_p).__c_ospeed
} }
#[cfg(target_os = "redox")]
#[no_mangle]
pub unsafe extern "C" fn cfgetospeed(termios_p: *const termios) -> speed_t {
//TODO
0
}
#[cfg(target_os = "linux")]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn cfsetispeed(termios_p: *mut termios, speed: speed_t) -> c_int { pub unsafe extern "C" fn cfsetispeed(termios_p: *mut termios, speed: speed_t) -> c_int {
match speed as usize { match speed as usize {
...@@ -172,12 +111,21 @@ pub unsafe extern "C" fn cfsetispeed(termios_p: *mut termios, speed: speed_t) -> ...@@ -172,12 +111,21 @@ pub unsafe extern "C" fn cfsetispeed(termios_p: *mut termios, speed: speed_t) ->
0 0
} }
_ => { _ => {
platform::errno = errno::EINVAL; platform::ERRNO.set(errno::EINVAL);
-1 -1
} }
} }
} }
#[cfg(target_os = "redox")]
#[no_mangle]
pub unsafe extern "C" fn cfsetispeed(termios_p: *mut termios, speed: speed_t) -> c_int {
//TODO
platform::ERRNO.set(errno::EINVAL);
-1
}
#[cfg(target_os = "linux")]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn cfsetospeed(termios_p: *mut termios, speed: speed_t) -> c_int { pub unsafe extern "C" fn cfsetospeed(termios_p: *mut termios, speed: speed_t) -> c_int {
match speed as usize { match speed as usize {
...@@ -186,12 +134,29 @@ pub unsafe extern "C" fn cfsetospeed(termios_p: *mut termios, speed: speed_t) -> ...@@ -186,12 +134,29 @@ pub unsafe extern "C" fn cfsetospeed(termios_p: *mut termios, speed: speed_t) ->
0 0
} }
_ => { _ => {
platform::errno = errno::EINVAL; platform::ERRNO.set(errno::EINVAL);
-1 -1
} }
} }
} }
#[cfg(target_os = "redox")]
#[no_mangle]
pub unsafe extern "C" fn cfsetospeed(termios_p: *mut termios, speed: speed_t) -> c_int {
//TODO
platform::ERRNO.set(errno::EINVAL);
-1
}
#[no_mangle]
pub unsafe extern "C" fn cfsetspeed(termios_p: *mut termios, speed: speed_t) -> c_int {
let r = cfsetispeed(termios_p, speed);
if r < 0 {
return r;
}
cfsetospeed(termios_p, speed)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tcflush(fd: c_int, queue: c_int) -> c_int { pub unsafe extern "C" fn tcflush(fd: c_int, queue: c_int) -> c_int {
sys_ioctl::ioctl(fd, sys_ioctl::TCFLSH, queue as *mut c_void) sys_ioctl::ioctl(fd, sys_ioctl::TCFLSH, queue as *mut c_void)
...@@ -209,9 +174,26 @@ pub unsafe extern "C" fn tcsendbreak(fd: c_int, _dur: c_int) -> c_int { ...@@ -209,9 +174,26 @@ pub unsafe extern "C" fn tcsendbreak(fd: c_int, _dur: c_int) -> c_int {
sys_ioctl::ioctl(fd, sys_ioctl::TCSBRK, 0 as *mut _) sys_ioctl::ioctl(fd, sys_ioctl::TCSBRK, 0 as *mut _)
} }
#[no_mangle]
pub unsafe extern "C" fn tcsetwinsize(fd: c_int, mut sws: winsize) -> c_int {
sys_ioctl::ioctl(fd, sys_ioctl::TIOCSWINSZ, &mut sws as *mut _ as *mut c_void)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tcflow(fd: c_int, action: c_int) -> c_int { pub unsafe extern "C" fn tcflow(fd: c_int, action: c_int) -> c_int {
// non-zero duration is ignored by musl due to it being // non-zero duration is ignored by musl due to it being
// implementation-defined. we do the same. // implementation-defined. we do the same.
sys_ioctl::ioctl(fd, sys_ioctl::TCXONC, action as *mut _) sys_ioctl::ioctl(fd, sys_ioctl::TCXONC, action as *mut _)
} }
#[no_mangle]
pub unsafe extern "C" fn cfmakeraw(termios_p: *mut termios) {
(*termios_p).c_iflag &=
!(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON) as u32;
(*termios_p).c_oflag &= !OPOST as u32;
(*termios_p).c_lflag &= !(ECHO | ECHONL | ICANON | ISIG | IEXTEN) as u32;
(*termios_p).c_cflag &= !(CSIZE | PARENB) as u32;
(*termios_p).c_cflag |= CS8 as u32;
(*termios_p).c_cc[VMIN] = 1;
(*termios_p).c_cc[VTIME] = 0;
}
/* c_cc { */
pub const VEOF: usize = 0;
pub const VEOL: usize = 1;
pub const VEOL2: usize = 2;
pub const VERASE: usize = 3;
pub const VWERASE: usize = 4;
pub const VKILL: usize = 5;
pub const VREPRINT: usize = 6;
pub const VSWTC: usize = 7;
pub const VINTR: usize = 8;
pub const VQUIT: usize = 9;
pub const VSUSP: usize = 10;
pub const VSTART: usize = 12;
pub const VSTOP: usize = 13;
pub const VLNEXT: usize = 14;
pub const VDISCARD: usize = 15;
pub const VMIN: usize = 16;
pub const VTIME: usize = 17;
pub const NCCS: usize = 32;
/* } c_cc */
/* c_iflag { */
pub const IGNBRK: usize = 0o000_001;
pub const BRKINT: usize = 0o000_002;
pub const IGNPAR: usize = 0o000_004;
pub const PARMRK: usize = 0o000_010;
pub const INPCK: usize = 0o000_020;
pub const ISTRIP: usize = 0o000_040;
pub const INLCR: usize = 0o000_100;
pub const IGNCR: usize = 0o000_200;
pub const ICRNL: usize = 0o000_400;
pub const IXON: usize = 0o001_000;
pub const IXOFF: usize = 0o002_000;
/* } c_iflag */
/* c_oflag { */
pub const OPOST: usize = 0o000_001;
pub const ONLCR: usize = 0o000_002;
pub const OLCUC: usize = 0o000_004;
pub const OCRNL: usize = 0o000_010;
pub const ONOCR: usize = 0o000_020;
pub const ONLRET: usize = 0o000_040;
pub const OFILL: usize = 0o0000_100;
pub const OFDEL: usize = 0o0000_200;
/* } c_oflag */
/* c_cflag { */
pub const B0: usize = 0o000_000;
pub const B50: usize = 0o000_001;
pub const B75: usize = 0o000_002;
pub const B110: usize = 0o000_003;
pub const B134: usize = 0o000_004;
pub const B150: usize = 0o000_005;
pub const B200: usize = 0o000_006;
pub const B300: usize = 0o000_007;
pub const B600: usize = 0o000_010;
pub const B1200: usize = 0o000_011;
pub const B1800: usize = 0o000_012;
pub const B2400: usize = 0o000_013;
pub const B4800: usize = 0o000_014;
pub const B9600: usize = 0o000_015;
pub const B19200: usize = 0o000_016;
pub const B38400: usize = 0o000_017;
pub const B57600: usize = 0o0_020;
pub const B115200: usize = 0o0_021;
pub const B230400: usize = 0o0_022;
pub const B460800: usize = 0o0_023;
pub const B500000: usize = 0o0_024;
pub const B576000: usize = 0o0_025;
pub const B921600: usize = 0o0_026;
pub const B1000000: usize = 0o0_027;
pub const B1152000: usize = 0o0_030;
pub const B1500000: usize = 0o0_031;
pub const B2000000: usize = 0o0_032;
pub const B2500000: usize = 0o0_033;
pub const B3000000: usize = 0o0_034;
pub const B3500000: usize = 0o0_035;
pub const B4000000: usize = 0o0_036;
pub const CSIZE: usize = 0o001_400;
pub const CS5: usize = 0o000_000;
pub const CS6: usize = 0o000_400;
pub const CS7: usize = 0o001_000;
pub const CS8: usize = 0o001_400;
pub const CSTOPB: usize = 0o002_000;
pub const CREAD: usize = 0o004_000;
pub const PARENB: usize = 0o010_000;
pub const PARODD: usize = 0o020_000;
pub const HUPCL: usize = 0o040_000;
pub const CLOCAL: usize = 0o0100000;
/* } c_clfag */
/* c_lflag { */
pub const ISIG: usize = 0x0000_0080;
pub const ICANON: usize = 0x0000_0100;
pub const ECHO: usize = 0x0000_0008;
pub const ECHOE: usize = 0x0000_0002;
pub const ECHOK: usize = 0x0000_0004;
pub const ECHONL: usize = 0x0000_0010;
pub const NOFLSH: usize = 0x8000_0000;
pub const TOSTOP: usize = 0x0040_0000;
pub const IEXTEN: usize = 0x0000_0400;
/* } c_lflag */
sys_includes = ["sys/types.h", "stdint.h", "stddef.h"] sys_includes = ["sys/types.h", "stdint.h", "stddef.h", "features.h"]
include_guard = "_TIME_H" include_guard = "_RELIBC_TIME_H"
language = "C" language = "C"
style = "Tag" style = "Tag"
no_includes = true no_includes = true
...@@ -7,7 +7,3 @@ cpp_compat = true ...@@ -7,7 +7,3 @@ cpp_compat = true
[enum] [enum]
prefix_with_name = true prefix_with_name = true
[defines]
"target_os = linux" = "__linux__"
"target_os = redox" = "__redox__"
...@@ -10,7 +10,7 @@ pub mod sys; ...@@ -10,7 +10,7 @@ pub mod sys;
#[path = "redox.rs"] #[path = "redox.rs"]
pub mod sys; pub mod sys;
pub(crate) const UTC: *const c_char = b"UTC\0" as *const u8 as *const c_char; pub(crate) const UTC: *const c_char = b"UTC\0".as_ptr().cast();
pub(crate) const DAY_NAMES: [&str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; pub(crate) const DAY_NAMES: [&str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
pub(crate) const MON_NAMES: [&str; 12] = [ pub(crate) const MON_NAMES: [&str; 12] = [
......
//! time implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html //! `time.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html>.
use crate::{ use crate::{
header::errno::EIO, c_str::{CStr, CString},
error::ResultExt,
fs::File,
header::{errno::EOVERFLOW, fcntl::O_RDONLY, stdlib::getenv, unistd::readlink},
io::Read,
platform::{self, types::*, Pal, Sys}, platform::{self, types::*, Pal, Sys},
sync::{Mutex, MutexGuard},
};
use alloc::{boxed::Box, collections::BTreeSet, string::String, vec::Vec};
use chrono::{
format::ParseErrorKind, offset::MappedLocalTime, DateTime, Datelike, FixedOffset, NaiveDate,
NaiveDateTime, Offset, ParseError, TimeZone, Timelike, Utc,
};
use chrono_tz::{OffsetComponents, OffsetName, Tz};
use core::{
cell::OnceCell,
convert::{TryFrom, TryInto},
mem, ptr,
}; };
use self::constants::*; pub use self::constants::*;
pub mod constants; pub mod constants;
mod strftime; mod strftime;
mod strptime;
pub use strptime::strptime;
const YEARS_PER_ERA: time_t = 400;
const DAYS_PER_ERA: time_t = 146097;
const SECS_PER_DAY: time_t = 24 * 60 * 60;
const UTC_STR: &core::ffi::CStr = c"UTC";
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html>.
#[repr(C)] #[repr(C)]
#[derive(Default)] #[derive(Clone, Copy, Default)]
pub struct timespec { pub struct timespec {
pub tv_sec: time_t, pub tv_sec: time_t,
pub tv_nsec: c_long, pub tv_nsec: c_long,
} }
impl timespec {
// TODO: Write test
pub fn subtract(later: timespec, earlier: timespec) -> Option<timespec> {
// TODO: Can tv_nsec be negative?
let later_nsec = c_ulong::try_from(later.tv_nsec).ok()?;
let earlier_nsec = c_ulong::try_from(earlier.tv_nsec).ok()?;
Some(if later_nsec > earlier_nsec {
timespec {
tv_sec: later.tv_sec.checked_sub(earlier.tv_sec)?,
tv_nsec: (later_nsec - earlier_nsec) as _,
}
} else {
timespec {
tv_sec: later.tv_sec.checked_sub(earlier.tv_sec)?.checked_sub(1)?,
tv_nsec: 1_000_000_000 - (earlier_nsec - later_nsec) as c_long,
}
})
}
}
#[cfg(target_os = "redox")] #[cfg(target_os = "redox")]
impl<'a> From<&'a timespec> for syscall::TimeSpec { impl<'a> From<&'a timespec> for syscall::TimeSpec {
fn from(tp: &timespec) -> Self { fn from(tp: &timespec) -> Self {
Self { Self {
tv_sec: tp.tv_sec, tv_sec: tp.tv_sec as _,
tv_nsec: tp.tv_nsec as i32, tv_nsec: tp.tv_nsec as _,
} }
} }
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html>.
#[repr(C)] #[repr(C)]
pub struct tm { pub struct tm {
pub tm_sec: c_int, pub tm_sec: c_int, // 0 - 60
pub tm_min: c_int, pub tm_min: c_int, // 0 - 59
pub tm_hour: c_int, pub tm_hour: c_int, // 0 - 23
pub tm_mday: c_int, pub tm_mday: c_int, // 1 - 31
pub tm_mon: c_int, pub tm_mon: c_int, // 0 - 11
pub tm_year: c_int, pub tm_year: c_int, // years since 1900
pub tm_wday: c_int, pub tm_wday: c_int, // 0 - 6 (Sunday - Saturday)
pub tm_yday: c_int, pub tm_yday: c_int, // 0 - 365
pub tm_isdst: c_int, pub tm_isdst: c_int, // >0 if DST, 0 if not, <0 if unknown
pub tm_gmtoff: c_long, pub tm_gmtoff: c_long, // offset from UTC in seconds
pub tm_zone: *const c_char, pub tm_zone: *const c_char, // timezone abbreviation
} }
unsafe impl Sync for tm {} unsafe impl Sync for tm {}
// The C Standard says that localtime and gmtime return the same pointer. // The C Standard says that localtime and gmtime return the same pointer.
static mut TM: tm = tm { static mut TM: tm = blank_tm();
tm_sec: 0,
tm_min: 0,
tm_hour: 0,
tm_mday: 0,
tm_mon: 0,
tm_year: 0,
tm_wday: 0,
tm_yday: 0,
tm_isdst: 0,
tm_gmtoff: 0,
tm_zone: UTC,
};
// The C Standard says that ctime and asctime return the same pointer. // The C Standard says that ctime and asctime return the same pointer.
static mut ASCTIME: [c_char; 26] = [0; 26]; static mut ASCTIME: [c_char; 26] = [0; 26];
#[repr(transparent)]
pub struct TzName([*mut c_char; 2]);
unsafe impl Sync for TzName {}
// Name storage for the `tm_zone` field.
static TIMEZONE_NAMES: Mutex<OnceCell<BTreeSet<CString>>> = Mutex::new(OnceCell::new());
// Hold `TIMEZONE_LOCK` when updating `tzname`, `timezone`, and `daylight`.
static TIMEZONE_LOCK: Mutex<(Option<CString>, Option<CString>)> = Mutex::new((None, None));
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html>.
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static mut daylight: c_int = 0;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html>.
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static mut timezone: c_long = 0;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html>.
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static mut tzname: TzName = TzName([ptr::null_mut(); 2]);
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static mut getdate_err: c_int = 0;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html>.
#[repr(C)] #[repr(C)]
pub struct itimerspec { pub struct itimerspec {
pub it_interval: timespec, pub it_interval: timespec,
pub it_value: timespec, pub it_value: timespec,
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html>.
pub struct sigevent; pub struct sigevent;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/asctime.html>.
///
/// # Deprecation
/// The `asctime()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 7.
#[deprecated]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn asctime(timeptr: *const tm) -> *mut c_char { pub unsafe extern "C" fn asctime(timeptr: *const tm) -> *mut c_char {
asctime_r(timeptr, &mut ASCTIME as *mut [i8; 26] as *mut i8) asctime_r(timeptr, ASCTIME.as_mut_ptr().cast())
} }
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime.html>.
///
/// # Deprecation
/// The `asctime_r()` was marked obsolescent in the Open Group Base
/// Specifications Issue 7, and removed in Issue 8.
#[deprecated]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn asctime_r(tm: *const tm, buf: *mut c_char) -> *mut c_char { pub unsafe extern "C" fn asctime_r(tm: *const tm, buf: *mut c_char) -> *mut c_char {
let tm = &*tm; let tm_sec = (*tm).tm_sec;
let result = core::fmt::write( let tm_min = (*tm).tm_min;
&mut platform::UnsafeStringWriter(buf as *mut u8), let tm_hour = (*tm).tm_hour;
let tm_mday = (*tm).tm_mday;
let tm_mon = (*tm).tm_mon;
let tm_year = (*tm).tm_year;
let tm_wday = (*tm).tm_wday;
/* Panic when we run into undefined behavior.
*
* POSIX says (since issue 7) that asctime()/asctime_r() cause UB
* when the tm member values would cause out-of-bounds array access
* or overflow the output buffer. This contrasts with ISO C11+,
* which specifies UB for any tm members being outside their normal
* ranges. While POSIX explicitly defers to the C standard in case
* of contradictions, the assertions below follow the interpretation
* that POSIX simply defines some of C's undefined behavior, rather
* than conflict with the ISO standard.
*
* Note that C's "%.2d" formatting, unlike Rust's "{:02}"
* formatting, does not count a minus sign against the two digits to
* print, meaning that we must reject all negative values for
* seconds, minutes and hours. However, C's "%3d" (for day-of-month)
* is similar to Rust's "{:3}".
*
* To avoid year overflow problems (in Rust, where numeric overflow
* is considered an error), we subtract 1900 from the endpoints,
* rather than adding to the tm_year value. POSIX' requirement that
* tm_year be at most {INT_MAX}-1990 is satisfied for all legal
* values of {INT_MAX} through the max-4-digit requirement on the
* year.
*
* The tm_mon and tm_wday fields are used for array access and thus
* will already cause a panic in Rust code when out of range.
* However, using the assertions below allows a consistent error
* message for all fields. */
const OUT_OF_RANGE_MESSAGE: &str = "tm member out of range";
assert!(0 <= tm_sec && tm_sec <= 99, "{}", OUT_OF_RANGE_MESSAGE);
assert!(0 <= tm_min && tm_min <= 99, "{}", OUT_OF_RANGE_MESSAGE);
assert!(0 <= tm_hour && tm_hour <= 99, "{}", OUT_OF_RANGE_MESSAGE);
assert!(-99 <= tm_mday && tm_mday <= 999, "{}", OUT_OF_RANGE_MESSAGE);
assert!(0 <= tm_mon && tm_mon <= 11, "{}", OUT_OF_RANGE_MESSAGE);
assert!(
-999 - 1900 <= tm_year && tm_year <= 9999 - 1900,
"{}",
OUT_OF_RANGE_MESSAGE
);
assert!(0 <= tm_wday && tm_wday <= 6, "{}", OUT_OF_RANGE_MESSAGE);
// At this point, we can safely use the values as given.
let write_result = core::fmt::write(
// buf may be either `*mut u8` or `*mut i8`
&mut platform::UnsafeStringWriter(buf.cast()),
format_args!( format_args!(
"{:.3} {:.3}{:3} {:02}:{:02}:{:02} {}\n", "{:.3} {:.3}{:3} {:02}:{:02}:{:02} {}\n",
DAY_NAMES[tm.tm_wday as usize], DAY_NAMES[usize::try_from(tm_wday).unwrap()],
MON_NAMES[tm.tm_mon as usize], MON_NAMES[usize::try_from(tm_mon).unwrap()],
tm.tm_mday as usize, tm_mday,
tm.tm_hour as usize, tm_hour,
tm.tm_min as usize, tm_min,
tm.tm_sec as usize, tm_sec,
(1900 + tm.tm_year) 1900 + tm_year
), ),
); );
match result { match write_result {
Ok(_) => buf, Ok(_) => buf,
Err(_) => { Err(_) => {
platform::errno = EIO; /* asctime()/asctime_r() or the equivalent sprintf() call
core::ptr::null_mut() * have no defined errno setting */
ptr::null_mut()
} }
} }
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn clock() -> clock_t { pub extern "C" fn clock() -> clock_t {
let mut ts: timespec = unsafe { core::mem::uninitialized() }; let mut ts = mem::MaybeUninit::<timespec>::uninit();
if clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mut ts) != 0 { if unsafe { clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts.as_mut_ptr()) } != 0 {
return -1; return -1;
} }
let ts = unsafe { ts.assume_init() };
if ts.tv_sec > time_t::max_value() / CLOCKS_PER_SEC let clocks =
|| ts.tv_nsec / (1_000_000_000 / CLOCKS_PER_SEC) ts.tv_sec * CLOCKS_PER_SEC as i64 + (ts.tv_nsec / (1_000_000_000 / CLOCKS_PER_SEC)) as i64;
> time_t::max_value() - CLOCKS_PER_SEC * ts.tv_sec match clock_t::try_from(clocks) {
{ Ok(ok) => ok,
return -1; Err(_err) => -1,
} }
ts.tv_sec * CLOCKS_PER_SEC + ts.tv_nsec / (1_000_000_000 / CLOCKS_PER_SEC)
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getcpuclockid.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn clock_getres(clock_id: clockid_t, res: *mut timespec) -> c_int { pub extern "C" fn clock_getcpuclockid(pid: pid_t, clock_id: *mut clockid_t) -> c_int {
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn clock_gettime(clock_id: clockid_t, tp: *mut timespec) -> c_int { pub unsafe extern "C" fn clock_getres(clock_id: clockid_t, tp: *mut timespec) -> c_int {
Sys::clock_getres(clock_id, tp)
.map(|()| 0)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html>.
#[no_mangle]
pub unsafe extern "C" fn clock_gettime(clock_id: clockid_t, tp: *mut timespec) -> c_int {
Sys::clock_gettime(clock_id, tp) Sys::clock_gettime(clock_id, tp)
.map(|()| 0)
.or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_nanosleep.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn clock_settime(clock_id: clockid_t, tp: *const timespec) -> c_int { pub extern "C" fn clock_nanosleep(
clock_id: clockid_t,
flags: c_int,
rqtp: *const timespec,
rmtp: *mut timespec,
) -> c_int {
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html>.
#[no_mangle]
pub unsafe extern "C" fn clock_settime(clock_id: clockid_t, tp: *const timespec) -> c_int {
Sys::clock_settime(clock_id, tp)
.map(|()| 0)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ctime.html>.
///
/// # Deprecation
/// The `ctime()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 7.
#[deprecated]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn ctime(clock: *const time_t) -> *mut c_char { pub unsafe extern "C" fn ctime(clock: *const time_t) -> *mut c_char {
asctime(localtime(clock)) asctime(localtime(clock))
} }
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/ctime.html>.
///
/// # Deprecation
/// The `ctime_r()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 7, and removed in Issue 8.
#[deprecated]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn ctime_r(clock: *const time_t, buf: *mut c_char) -> *mut c_char { pub unsafe extern "C" fn ctime_r(clock: *const time_t, buf: *mut c_char) -> *mut c_char {
let mut tm1 : tm = core::mem::uninitialized(); // Using MaybeUninit<tm> seems to cause a panic during the build process
let mut tm1 = blank_tm();
localtime_r(clock, &mut tm1); localtime_r(clock, &mut tm1);
asctime_r(&mut tm1, buf) asctime_r(&tm1, buf)
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/difftime.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn difftime(time1: time_t, time0: time_t) -> c_double { pub unsafe extern "C" fn difftime(time1: time_t, time0: time_t) -> c_double {
(time1 - time0) as c_double (time1 - time0) as _
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getdate.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn getdate(string: *const c_char) -> tm { pub unsafe extern "C" fn getdate(string: *const c_char) -> *const tm {
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/gmtime.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn gmtime(timer: *const time_t) -> *mut tm { pub unsafe extern "C" fn gmtime(timer: *const time_t) -> *mut tm {
gmtime_r(timer, &mut TM) gmtime_r(timer, &mut TM)
} }
const MONTH_DAYS: [[c_int; 12]; 2] = [ /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/gmtime.html>.
// Non-leap years:
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
// Leap years:
[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
];
#[inline(always)]
fn leap_year(year: c_int) -> bool {
year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn gmtime_r(clock: *const time_t, t: *mut tm) -> *mut tm { pub unsafe extern "C" fn gmtime_r(clock: *const time_t, result: *mut tm) -> *mut tm {
let clock = *clock; let _ = get_localtime(*clock, result);
result
let mut day = (clock / (60 * 60 * 24)) as c_int;
if clock < 0 && clock % (60 * 60 * 24) != 0 {
// -1 because for negative values round upwards
// -0.3 == 0, but we want -1
day -= 1;
}
(*t).tm_sec = (clock % 60) as c_int;
(*t).tm_min = ((clock / 60) % 60) as c_int;
(*t).tm_hour = ((clock / (60 * 60)) % 24) as c_int;
while (*t).tm_sec < 0 {
(*t).tm_sec += 60;
(*t).tm_min -= 1;
}
while (*t).tm_min < 0 {
(*t).tm_min += 60;
(*t).tm_hour -= 1;
}
while (*t).tm_hour < 0 {
(*t).tm_hour += 24;
}
// Jan 1th was a thursday, 4th of a zero-indexed week.
(*t).tm_wday = (day + 4) % 7;
if (*t).tm_wday < 0 {
(*t).tm_wday += 7;
}
let mut year = 1970;
if day < 0 {
while day < 0 {
let days_in_year = if leap_year(year) { 366 } else { 365 };
day += days_in_year;
year -= 1;
}
(*t).tm_year = year - 1900;
(*t).tm_yday = day + 1;
} else {
loop {
let days_in_year = if leap_year(year) { 366 } else { 365 };
if day < days_in_year {
break;
}
day -= days_in_year;
year += 1;
}
(*t).tm_year = year - 1900;
(*t).tm_yday = day;
}
let leap = if leap_year(year) { 1 } else { 0 };
(*t).tm_mon = 0;
loop {
let days_in_month = MONTH_DAYS[leap][(*t).tm_mon as usize];
if day < days_in_month {
break;
}
day -= days_in_month;
(*t).tm_mon += 1;
}
(*t).tm_mday = 1 + day as c_int;
(*t).tm_isdst = 0;
(*t).tm_gmtoff = 0;
(*t).tm_zone = UTC;
t
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/localtime.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn localtime(clock: *const time_t) -> *mut tm { pub unsafe extern "C" fn localtime(clock: *const time_t) -> *mut tm {
localtime_r(clock, &mut TM) localtime_r(clock, &mut TM)
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/localtime.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn localtime_r(clock: *const time_t, t: *mut tm) -> *mut tm { pub unsafe extern "C" fn localtime_r(clock: *const time_t, t: *mut tm) -> *mut tm {
// TODO: Change tm_isdst, tm_gmtoff, tm_zone let mut lock = TIMEZONE_LOCK.lock();
gmtime_r(clock, t) clear_timezone(&mut lock);
if let (Some(std_time), dst_time) = get_localtime(*clock, t) {
set_timezone(&mut lock, &std_time, dst_time);
}
t
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mktime.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn mktime(t: *mut tm) -> time_t { pub unsafe extern "C" fn mktime(timeptr: *mut tm) -> time_t {
let mut year = (*t).tm_year + 1900; let mut lock = TIMEZONE_LOCK.lock();
let mut month = (*t).tm_mon; clear_timezone(&mut lock);
let mut day = (*t).tm_mday as i64 - 1;
let year = (*timeptr).tm_year + 1900;
let leap = if leap_year(year) { 1 } else { 0 }; let month = ((*timeptr).tm_mon + 1) as _;
let day = (*timeptr).tm_mday as _;
if year < 1970 { let hour = (*timeptr).tm_hour as _;
day = MONTH_DAYS[if leap_year(year) { 1 } else { 0 }][(*t).tm_mon as usize] as i64 - day; let minute = (*timeptr).tm_min as _;
let second = (*timeptr).tm_sec as _;
while year < 1969 {
year += 1; let naive_local = match NaiveDate::from_ymd_opt(year, month, day)
day += if leap_year(year) { 366 } else { 365 }; .and_then(|date| date.and_hms_opt(hour, minute, second))
{
Some(datetime) => datetime,
None => {
platform::ERRNO.set(EOVERFLOW);
return -1;
} }
};
while month < 11 { let offset = get_offset((*timeptr).tm_gmtoff).unwrap();
month += 1; let tz = time_zone();
day += MONTH_DAYS[leap][month as usize] as i64; // Create DateTime<FixedOffset>
let datetime = match offset.from_local_datetime(&naive_local) {
MappedLocalTime::Single(datetime) => datetime,
_ => {
platform::ERRNO.set(EOVERFLOW);
return -1;
} }
};
-(day * (60 * 60 * 24) // Convert to UTC and get timestamp
- (((*t).tm_hour as i64) * (60 * 60) + ((*t).tm_min as i64) * 60 + (*t).tm_sec as i64)) let tz_datetime = datetime.with_timezone(&tz);
} else { let timestamp = tz_datetime.timestamp();
while year > 1970 {
year -= 1;
day += if leap_year(year) { 366 } else { 365 };
}
while month > 0 { ptr::write(timeptr, datetime_to_tm(&tz_datetime));
month -= 1;
day += MONTH_DAYS[leap][month as usize] as i64;
}
(day * (60 * 60 * 24) // Convert UTC time to local time
+ ((*t).tm_hour as i64) * (60 * 60) if let (std_time, dst_time) = match tz.timestamp_opt(timestamp, 0) {
+ ((*t).tm_min as i64) * 60 MappedLocalTime::Single(t) => (t, None),
+ (*t).tm_sec as i64) // This variant contains the two possible results, in the order (earliest, latest).
MappedLocalTime::Ambiguous(t1, t2) => (t2, Some(t1)),
MappedLocalTime::None => return timestamp,
} {
set_timezone(&mut lock, &std_time, dst_time);
} }
timestamp
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/nanosleep.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int { pub unsafe extern "C" fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int {
Sys::nanosleep(rqtp, rmtp) Sys::nanosleep(rqtp, rmtp).map(|()| 0).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strftime.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn strftime( pub unsafe extern "C" fn strftime(
s: *mut c_char, s: *mut c_char,
...@@ -328,11 +438,14 @@ pub unsafe extern "C" fn strftime( ...@@ -328,11 +438,14 @@ pub unsafe extern "C" fn strftime(
} }
} }
// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strftime.html>.
// TODO: needs locale_t
// #[no_mangle] // #[no_mangle]
pub extern "C" fn strptime(buf: *const c_char, format: *const c_char, tm: *mut tm) -> *mut c_char { /*pub extern "C" fn strftime_l(s: *mut char, maxsize: size_t, format: *const c_char, timeptr: *const tm, locale: locale_t) -> size_t {
unimplemented!(); unimplemented!();
} }*/
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/time.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn time(tloc: *mut time_t) -> time_t { pub unsafe extern "C" fn time(tloc: *mut time_t) -> time_t {
let mut ts = timespec::default(); let mut ts = timespec::default();
...@@ -343,30 +456,46 @@ pub unsafe extern "C" fn time(tloc: *mut time_t) -> time_t { ...@@ -343,30 +456,46 @@ pub unsafe extern "C" fn time(tloc: *mut time_t) -> time_t {
ts.tv_sec ts.tv_sec
} }
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/timegm.3.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn timelocal(tm: *mut tm) -> time_t { pub unsafe extern "C" fn timegm(tm: *mut tm) -> time_t {
//TODO: timezone let tm_val = &mut *tm;
timegm(tm) let dt = match convert_tm_generic(&Utc, tm_val) {
Some(dt) => dt,
None => return -1,
};
(*tm).tm_wday = dt.weekday().num_days_from_sunday() as _;
(*tm).tm_yday = dt.ordinal0() as _; // day of year starting at 0
(*tm).tm_isdst = 0; // UTC does not use DST
(*tm).tm_gmtoff = 0; // UTC offset is zero
(*tm).tm_zone = UTC_STR.as_ptr() as *const c_char;
dt.timestamp()
} }
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/timegm.3.html>.
#[deprecated]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn timegm(tm: *mut tm) -> time_t { pub unsafe extern "C" fn timelocal(tm: *mut tm) -> time_t {
let mut y = (*tm).tm_year as time_t + 1900; let tm_val = &mut *tm;
let mut m = (*tm).tm_mon as time_t + 1; let tz = time_zone();
if m <= 2 { let dt = match convert_tm_generic(&tz, tm_val) {
y -= 1; Some(dt) => dt,
m += 12; None => return -1,
} };
let d = (*tm).tm_mday as time_t;
let h = (*tm).tm_hour as time_t; let tz_name = CString::new(tz.name()).unwrap();
let mi = (*tm).tm_min as time_t; (*tm).tm_wday = dt.weekday().num_days_from_sunday() as _;
let s = (*tm).tm_sec as time_t; (*tm).tm_yday = dt.ordinal0() as _; // day of year starting at 0
(365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - 719561) * 86400 (*tm).tm_isdst = dt.offset().dst_offset().num_hours() as _;
+ 3600 * h (*tm).tm_gmtoff = dt.offset().fix().local_minus_utc() as _;
+ 60 * mi (*tm).tm_zone = tz_name.into_raw().cast();
+ s
dt.timestamp()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/timer_create.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn timer_create( pub extern "C" fn timer_create(
clock_id: clockid_t, clock_id: clockid_t,
...@@ -376,16 +505,25 @@ pub extern "C" fn timer_create( ...@@ -376,16 +505,25 @@ pub extern "C" fn timer_create(
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/timer_delete.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn timer_delete(timerid: timer_t) -> c_int { pub extern "C" fn timer_delete(timerid: timer_t) -> c_int {
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/timer_getoverrun.html>.
// #[no_mangle]
pub extern "C" fn timer_getoverrun(timerid: timer_t) -> c_int {
unimplemented!();
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/timer_getoverrun.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn tzset() { pub extern "C" fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> c_int {
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/timer_getoverrun.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn timer_settime( pub extern "C" fn timer_settime(
timerid: timer_t, timerid: timer_t,
...@@ -396,19 +534,219 @@ pub extern "C" fn timer_settime( ...@@ -396,19 +534,219 @@ pub extern "C" fn timer_settime(
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/timespec_get.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> c_int { pub extern "C" fn timespec_get(ts: *mut timespec, base: c_int) -> c_int {
unimplemented!(); unimplemented!();
} }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tzset.html>.
pub extern "C" fn timer_getoverrun(timerid: timer_t) -> c_int { #[no_mangle]
unimplemented!(); pub unsafe extern "C" fn tzset() {
let mut lock = TIMEZONE_LOCK.lock();
unsafe { clear_timezone(&mut lock) };
let tz = time_zone();
let datetime = now();
let (std_time, dst_time) = match tz.from_local_datetime(&datetime) {
MappedLocalTime::Single(t) => (t, None),
// This variant contains the two possible results, in the order (earliest, latest).
MappedLocalTime::Ambiguous(t1, t2) => (t2, Some(t1)),
MappedLocalTime::None => return,
};
set_timezone(&mut lock, &std_time, dst_time)
}
fn convert_tm_generic<Tz: TimeZone>(tz: &Tz, tm_val: &tm) -> Option<DateTime<Tz>> {
// Adjust fields: tm_year is years since 1900; tm_mon is 0-indexed.
let year = tm_val.tm_year + 1900;
let month = tm_val.tm_mon + 1; // convert to 1-indexed
let day = tm_val.tm_mday;
let hour = tm_val.tm_hour;
let minute = tm_val.tm_min;
let second = tm_val.tm_sec;
match tz.with_ymd_and_hms(
year,
month as u32,
day as u32,
hour as u32,
minute as u32,
second as u32,
) {
MappedLocalTime::Single(dt) => Some(dt),
MappedLocalTime::Ambiguous(dt1, _dt2) => Some(dt1), // choose the earliest value
_ => None,
}
} }
/* fn clear_timezone(guard: &mut MutexGuard<'_, (Option<CString>, Option<CString>)>) {
#[no_mangle] guard.0 = None;
pub extern "C" fn func(args) -> c_int { guard.1 = None;
unimplemented!(); unsafe {
tzname.0[0] = ptr::null_mut();
tzname.0[1] = ptr::null_mut();
timezone = 0;
daylight = 0;
}
}
#[inline(always)]
fn get_system_time_zone<'a>() -> Option<&'a str> {
// Resolve the symlink for localtime
const BSIZE: size_t = 100;
let mut buffer: [u8; BSIZE] = [0; BSIZE];
#[cfg(not(target_os = "redox"))]
let (localtime, prefix) = (c"/etc/localtime", "/usr/share/zoneinfo/");
#[cfg(target_os = "redox")]
let (localtime, prefix) = (c"/etc/localtime", "/usr/share/zoneinfo/");
if unsafe { readlink(localtime.as_ptr().cast(), buffer.as_mut_ptr().cast(), BSIZE) } == -1 {
return None;
}
let path = unsafe { CStr::from_ptr(buffer.as_mut_ptr().cast()) };
if let Ok(tz_name) = path.to_str() {
if let Some(stripped) = tz_name.strip_prefix(prefix) {
return Some(stripped);
}
}
None
}
fn get_current_time_zone<'a>() -> &'a str {
// Check the `TZ` environment variable
let tz_env = unsafe { getenv(c"TZ".as_ptr() as _) };
if !tz_env.is_null() {
if let Ok(tz) = unsafe { CStr::from_ptr(tz_env) }.to_str() {
return tz;
}
}
// Fallback to the system's default time zone
if let Some(tz) = get_system_time_zone() {
return tz;
}
// If all else fails, use UTC
"UTC"
}
#[inline(always)]
fn time_zone() -> Tz {
get_current_time_zone().parse().unwrap_or(Tz::UTC)
}
#[inline(always)]
fn now() -> NaiveDateTime {
let mut now = timespec::default();
unsafe {
Sys::clock_gettime(CLOCK_REALTIME, &mut now);
}
NaiveDateTime::from_timestamp(now.tv_sec, now.tv_nsec as _)
}
#[inline(always)]
fn get_localtime(clock: time_t, t: *mut tm) -> (Option<DateTime<Tz>>, Option<DateTime<Tz>>) {
let tz = time_zone();
// Convert UTC time to local time
let (std_time, dst_time) = match tz.timestamp_opt(clock, 0) {
MappedLocalTime::Single(t) => (Some(t), None),
// This variant contains the two possible results, in the order (earliest, latest).
MappedLocalTime::Ambiguous(t1, t2) => (Some(t2), Some(t1)),
MappedLocalTime::None => return (None, None),
};
unsafe { ptr::write(t, datetime_to_tm(&std_time.unwrap())) };
(std_time, dst_time)
}
unsafe fn datetime_to_tm(local_time: &DateTime<Tz>) -> tm {
let tz = local_time.timezone().name();
let tz = tz.strip_prefix("Etc/").or(Some(tz)).unwrap();
let mut t = blank_tm();
// Populate the `tm` structure
t.tm_sec = local_time.second() as _;
t.tm_min = local_time.minute() as _;
t.tm_hour = local_time.hour() as _;
t.tm_mday = local_time.day() as _;
t.tm_mon = local_time.month0() as _; // 0-based month
t.tm_year = (local_time.year() - 1900) as _; // Years since 1900
t.tm_wday = local_time.weekday().num_days_from_sunday() as _;
t.tm_yday = local_time.ordinal0() as _; // 0-based day of year
let offset = local_time.offset();
t.tm_isdst = offset.dst_offset().num_hours() as _;
// Get the UTC offset in seconds
t.tm_gmtoff = offset.fix().local_minus_utc() as _;
let tm_zone = {
let mut timezone_names = TIMEZONE_NAMES.lock();
timezone_names.get_or_init(BTreeSet::new);
let cstr = CString::new(tz).unwrap();
timezone_names.get_mut().unwrap().insert(cstr.clone());
timezone_names.get().unwrap().get(&cstr).unwrap().as_ptr()
};
t.tm_zone = tm_zone.cast();
t
}
unsafe fn set_timezone(
guard: &mut MutexGuard<'_, (Option<CString>, Option<CString>)>,
std: &DateTime<Tz>,
dst: Option<DateTime<Tz>>,
) {
let ut_offset = std.offset();
guard.0 = Some(CString::new(ut_offset.abbreviation().expect("Wrong timezone")).unwrap());
tzname.0[0] = guard.0.as_ref().unwrap().as_ptr().cast_mut();
match dst {
Some(dst) => {
guard.1 =
Some(CString::new(dst.offset().abbreviation().expect("Wrong timezone")).unwrap());
tzname.0[1] = guard.1.as_ref().unwrap().as_ptr().cast_mut();
daylight = 1;
}
None => {
guard.1 = None;
tzname.0[1] = guard.0.as_ref().unwrap().as_ptr().cast_mut();
daylight = 0;
}
}
timezone = -c_long::from(ut_offset.fix().local_minus_utc());
}
#[inline(always)]
pub const fn get_offset(off: c_long) -> Option<FixedOffset> {
if off < 0 {
FixedOffset::west_opt(off as _)
} else {
FixedOffset::east_opt(off as _)
}
}
const fn blank_tm() -> tm {
tm {
tm_year: 0,
tm_mon: 0,
tm_mday: 0,
tm_hour: 0,
tm_min: 0,
tm_sec: 0,
tm_wday: 0,
tm_yday: 0,
tm_isdst: -1,
tm_gmtoff: 0,
tm_zone: ptr::null_mut(),
}
} }
*/
// strftime implementation for Redox, following the POSIX standard.
// Following https://pubs.opengroup.org/onlinepubs/7908799/xsh/strftime.html
use alloc::string::String; use alloc::string::String;
use crate::platform::{self, types::*, WriteByte}; use super::{get_offset, tm};
use crate::{
c_str::CStr,
platform::{self, types::*, WriteByte},
};
use super::tm; // We use the langinfo constants
use crate::header::langinfo::{
nl_item,
nl_langinfo,
ABDAY_1,
ABMON_1,
AM_STR,
DAY_1,
MON_1,
PM_STR,
// TODO : other constants if needed
};
/// A helper that calls `nl_langinfo(item)` and converts the returned pointer
/// into a `&str`. If it fails or is null, returns an empty string "".
unsafe fn langinfo_to_str(item: nl_item) -> &'static str {
use core::ffi::CStr;
let ptr = nl_langinfo(item);
if ptr.is_null() {
return "";
}
match CStr::from_ptr(ptr).to_str() {
Ok(s) => s,
Err(_) => "",
}
}
/// Formats time data according to the given `format` string.
///
/// Use `langinfo` for locale-based day/month names,
/// but still hard-codes other aspects of the "C" locale (like numeric/date
/// formats) and ignores `%E` / `%O` variations.
pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const tm) -> size_t { pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const tm) -> size_t {
/// Helper that actually parses the format string and writes output.
pub unsafe fn inner_strftime<W: WriteByte>( pub unsafe fn inner_strftime<W: WriteByte>(
w: &mut W, w: &mut W,
mut format: *const c_char, mut format: *const c_char,
...@@ -22,11 +60,11 @@ pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const ...@@ -22,11 +60,11 @@ pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const
} }
}}; }};
(recurse $fmt:expr) => {{ (recurse $fmt:expr) => {{
let mut fmt = String::with_capacity($fmt.len() + 1); let mut tmp = String::with_capacity($fmt.len() + 1);
fmt.push_str($fmt); tmp.push_str($fmt);
fmt.push('\0'); tmp.push('\0');
if !inner_strftime(w, fmt.as_ptr() as *mut c_char, t) { if !inner_strftime(w, tmp.as_ptr() as *mut c_char, t) {
return false; return false;
} }
}}; }};
...@@ -36,106 +74,222 @@ pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const ...@@ -36,106 +74,222 @@ pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const
} }
}}; }};
($fmt:expr, $($args:expr),+) => {{ ($fmt:expr, $($args:expr),+) => {{
// Would use write!() if I could get the length written // Could use write!() if we didn't need the exact count
if write!(w, $fmt, $($args),+).is_err() { if write!(w, $fmt, $($args),+).is_err() {
return false; return false;
} }
}}; }};
} }
const WDAYS: [&str; 7] = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
const MONTHS: [&str; 12] = [
"January",
"Febuary",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
while *format != 0 { while *format != 0 {
// If the character isn't '%', just copy it out.
if *format as u8 != b'%' { if *format as u8 != b'%' {
w!(byte * format as u8); w!(byte * format as u8);
format = format.offset(1); format = format.offset(1);
continue; continue;
} }
// Skip '%'
format = format.offset(1); format = format.offset(1);
// POSIX says '%E' and '%O' can modify numeric formats for locales,
// but we ignore them in this minimal "C" locale approach.
if *format as u8 == b'E' || *format as u8 == b'O' { if *format as u8 == b'E' || *format as u8 == b'O' {
// Ignore because these do nothing without locale
format = format.offset(1); format = format.offset(1);
} }
match *format as u8 { match *format as u8 {
// Literal '%'
b'%' => w!(byte b'%'), b'%' => w!(byte b'%'),
// Newline and tab expansions
b'n' => w!(byte b'\n'), b'n' => w!(byte b'\n'),
b't' => w!(byte b'\t'), b't' => w!(byte b'\t'),
b'a' => w!(&WDAYS[(*t).tm_wday as usize][..3]),
b'A' => w!(WDAYS[(*t).tm_wday as usize]), // Abbreviated weekday name: %a
b'b' | b'h' => w!(&MONTHS[(*t).tm_mon as usize][..3]), b'a' => {
b'B' => w!(MONTHS[(*t).tm_mon as usize]), // `ABDAY_1 + tm_wday` is the correct langinfo ID for abbreviated weekdays
let s = langinfo_to_str(ABDAY_1 + (*t).tm_wday as i32);
w!(s);
}
// Full weekday name: %A
b'A' => {
// `DAY_1 + tm_wday` is the correct langinfo ID for full weekdays
let s = langinfo_to_str(DAY_1 + (*t).tm_wday as i32);
w!(s);
}
// Abbreviated month name: %b or %h
b'b' | b'h' => {
let s = langinfo_to_str(ABMON_1 + (*t).tm_mon as i32);
w!(s);
}
// Full month name: %B
b'B' => {
let s = langinfo_to_str(MON_1 + (*t).tm_mon as i32);
w!(s);
}
// Century: %C
b'C' => { b'C' => {
let mut year = (*t).tm_year / 100; let mut year = (*t).tm_year / 100;
// Round up
if (*t).tm_year % 100 != 0 { if (*t).tm_year % 100 != 0 {
year += 1; year += 1;
} }
w!("{:02}", year + 19); w!("{:02}", year + 19);
} }
// Day of month: %d
b'd' => w!("{:02}", (*t).tm_mday), b'd' => w!("{:02}", (*t).tm_mday),
// %D => same as %m/%d/%y
b'D' => w!(recurse "%m/%d/%y"), b'D' => w!(recurse "%m/%d/%y"),
// Day of month, space-padded: %e
b'e' => w!("{:2}", (*t).tm_mday), b'e' => w!("{:2}", (*t).tm_mday),
// ISO 8601 date: %F => %Y-%m-%d
b'F' => w!(recurse "%Y-%m-%d"), b'F' => w!(recurse "%Y-%m-%d"),
// Hour (00-23): %H
b'H' => w!("{:02}", (*t).tm_hour), b'H' => w!("{:02}", (*t).tm_hour),
// Hour (01-12): %I
b'I' => w!("{:02}", ((*t).tm_hour + 12 - 1) % 12 + 1), b'I' => w!("{:02}", ((*t).tm_hour + 12 - 1) % 12 + 1),
b'j' => w!("{:03}", (*t).tm_yday),
// Day of year: %j
b'j' => w!("{:03}", (*t).tm_yday + 1),
// etc.
b'k' => w!("{:2}", (*t).tm_hour), b'k' => w!("{:2}", (*t).tm_hour),
b'l' => w!("{:2}", ((*t).tm_hour + 12 - 1) % 12 + 1), b'l' => w!("{:2}", ((*t).tm_hour + 12 - 1) % 12 + 1),
b'm' => w!("{:02}", (*t).tm_mon + 1), b'm' => w!("{:02}", (*t).tm_mon + 1),
b'M' => w!("{:02}", (*t).tm_min), b'M' => w!("{:02}", (*t).tm_min),
b'p' => w!(if (*t).tm_hour < 12 { "AM" } else { "PM" }),
b'P' => w!(if (*t).tm_hour < 12 { "am" } else { "pm" }), // AM/PM (uppercase): %p
b'p' => {
// Get "AM" / "PM" from langinfo
if (*t).tm_hour < 12 {
w!(langinfo_to_str(AM_STR));
} else {
w!(langinfo_to_str(PM_STR));
}
}
// am/pm (lowercase): %P
b'P' => {
// Convert the AM_STR / PM_STR to lowercase
if (*t).tm_hour < 12 {
let am = langinfo_to_str(AM_STR).to_ascii_lowercase();
w!(&am);
} else {
let pm = langinfo_to_str(PM_STR).to_ascii_lowercase();
w!(&pm);
}
}
// 12-hour clock with seconds + AM/PM: %r => %I:%M:%S %p
b'r' => w!(recurse "%I:%M:%S %p"), b'r' => w!(recurse "%I:%M:%S %p"),
// 24-hour clock without seconds: %R => %H:%M
b'R' => w!(recurse "%H:%M"), b'R' => w!(recurse "%H:%M"),
// Nothing is modified in mktime, but the C standard of course requires a mutable pointer ._.
// Seconds since the Epoch: %s => calls mktime() to convert tm to time_t
b's' => w!("{}", super::mktime(t as *mut tm)), b's' => w!("{}", super::mktime(t as *mut tm)),
// Seconds (00-60): %S (unchanged)
b'S' => w!("{:02}", (*t).tm_sec), b'S' => w!("{:02}", (*t).tm_sec),
// 24-hour clock with seconds: %T => %H:%M:%S
b'T' => w!(recurse "%H:%M:%S"), b'T' => w!(recurse "%H:%M:%S"),
// Weekday (1-7, Monday=1): %u
b'u' => w!("{}", ((*t).tm_wday + 7 - 1) % 7 + 1), b'u' => w!("{}", ((*t).tm_wday + 7 - 1) % 7 + 1),
// Sunday-based week of year: %U
b'U' => w!("{}", ((*t).tm_yday + 7 - (*t).tm_wday) / 7), b'U' => w!("{}", ((*t).tm_yday + 7 - (*t).tm_wday) / 7),
// ISO-8601 week of year
b'V' => w!("{}", week_of_year(unsafe { &*t })),
// Weekday (0-6, Sunday=0): %w
b'w' => w!("{}", (*t).tm_wday), b'w' => w!("{}", (*t).tm_wday),
// Monday-based week of year: %W
b'W' => w!("{}", ((*t).tm_yday + 7 - ((*t).tm_wday + 6) % 7) / 7), b'W' => w!("{}", ((*t).tm_yday + 7 - ((*t).tm_wday + 6) % 7) / 7),
// Last two digits of year: %y
b'y' => w!("{:02}", (*t).tm_year % 100), b'y' => w!("{:02}", (*t).tm_year % 100),
// Full year: %Y
b'Y' => w!("{}", (*t).tm_year + 1900), b'Y' => w!("{}", (*t).tm_year + 1900),
b'z' => w!("+0000"), // TODO
b'Z' => w!("UTC"), // TODO // Timezone offset: %z
b'z' => {
let offset = (*t).tm_gmtoff;
let (sign, offset) = if offset < 0 {
('-', -offset)
} else {
('+', offset)
};
let mins = offset.div_euclid(60);
let min = mins.rem_euclid(60);
let hour = mins.div_euclid(60);
w!("{}{:02}{:02}", sign, hour, min)
}
// Timezone name: %Z
b'Z' => w!("{}", CStr::from_ptr((*t).tm_zone).to_str().unwrap()),
// Date+time+TZ: %+
b'+' => w!(recurse "%a %b %d %T %Z %Y"), b'+' => w!(recurse "%a %b %d %T %Z %Y"),
// Unrecognized format specifier => fail
_ => return false, _ => return false,
} }
// Move past the format specifier
format = format.offset(1); format = format.offset(1);
} }
true true
} }
let mut w = platform::CountingWriter::new(w); // Wrap the writer in a CountingWriter to return how many bytes were written.
if !inner_strftime(&mut w, format, t) { let mut cw = platform::CountingWriter::new(w);
if !inner_strftime(&mut cw, format, t) {
return 0; return 0;
} }
cw.written
}
/// Calculate number of weeks in a year as defined by ISO 8601
///
/// ## Source
/// https://en.wikipedia.org/wiki/ISO_week_date
fn weeks_per_year(year: c_int) -> c_int {
let year = year as f64;
let p_y = (year + (year / 4.) - (year / 100.) + (year / 400.)) as c_int % 7;
if p_y == 4 {
53
} else {
52
}
}
/// Calculate the week of the year accounting for leap weeks (ISO 8601)
///
/// ## Source
/// https://en.wikipedia.org/wiki/ISO_week_date
fn week_of_year(time: &tm) -> c_int {
let week = (10 + time.tm_yday - time.tm_wday) / 7;
w.written if week <= 1 {
weeks_per_year(time.tm_year - 1)
} else if week > weeks_per_year(time.tm_year) {
1
} else {
week
}
} }
// 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, c_void, CStr},
mem::MaybeUninit,
ptr,
ptr::NonNull,
slice, str,
};
/// For convenience, we define some helper constants for the C-locale.
const SHORT_DAYS: [&str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const LONG_DAYS: [&str; 7] = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
const SHORT_MONTHS: [&str; 12] = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
];
const LONG_MONTHS: [&str; 12] = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strptime.html>.
#[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();
};
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
}
};
// Zero-initialize the output `tm` structure
// (equivalent to: tm_sec=0, tm_min=0, tm_hour=0...)
unsafe {
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;
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;
}
// 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;
}
}
///////////////////////////
// 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;
}
///////////////////////////
// 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) => unsafe { v },
None => return ptr::null_mut(),
};
unsafe {
(*tm).tm_mday = val as c_int;
// Day of month is limited to [1,31] according to the standard
if (*tm).tm_mday < 1 || (*tm).tm_mday > 31 {
return ptr::null_mut();
}
}
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,...)
unsafe {
(*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;
}
//////////////////////////////
// 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 };
unsafe {
(*tm).tm_year = (fullyear - 1900) 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(),
};
unsafe {
(*tm).tm_year = (val as c_int) - 1900;
}
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();
}
unsafe {
(*tm).tm_hour = 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();
}
unsafe {
(*tm).tm_hour = val as c_int;
}
// We’ll interpret AM/PM with %p if it appears later
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();
}
unsafe {
(*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();
}
unsafe {
(*tm).tm_sec = val as c_int;
}
index_in_input += len;
}
///////////////////////////
// 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 unsafe { (*tm).tm_hour } == 12 {
// 12 AM => 00:xx, 12 PM => 12:xx
unsafe {
(*tm).tm_hour = if is_pm { 12 } else { 0 };
}
} else {
// 1..11 AM => 1..11, 1..11 PM => 13..23
if is_pm {
unsafe {
(*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)) => {
unsafe {
(*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)) => {
unsafe {
(*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)) => {
unsafe {
(*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)) => {
unsafe {
(*tm).tm_mon = mon as c_int;
}
used
}
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();
}
// store in tm_yday
unsafe {
(*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 unsafe { 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 unsafe { apply_subformat(&input_str[index_in_input..], subfmt, tm) } {
Some(v) => v,
None => return ptr::null_mut(),
};
index_in_input += used;
}
'T' => {
// Equivalent to %H:%M:%S
let subfmt = "%H:%M:%S";
let used =
match unsafe { apply_subformat(&input_str[index_in_input..], subfmt, tm) } {
Some(v) => v,
None => return ptr::null_mut(),
};
index_in_input += used;
}
//////////////////////////////////////////////////////////
// TODO : not implemented: %x, %X, %c, %r, %R, etc. //
//////////////////////////////////////////////////////////
// Hint : if you want to implement these, do similarly to %D / %F (or parse manually)
'x' | 'X' | 'c' | 'r' | 'R' => {
// Return NULL if we don’t want to accept them :
return ptr::null_mut();
}
///////////////////////////
// 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 = unsafe { buf.add(index_in_input) };
ret_ptr as *mut c_char
}
// -----------------------
// Helper / Parsing Logic
// -----------------------
/// 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, width: usize, allow_variable: bool) -> Option<(i32, usize)> {
let mut val = 0i32;
let mut chars = input.chars();
let mut count = 0;
while let Some(c) = chars.next() {
if !c.is_ascii_digit() {
break;
}
// 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 {
break;
}
}
if count == 0 {
None
} else {
Some((val, count))
}
}
/// Handle AM/PM. Returns (is_pm, length_consumed).
/// Accepts "AM", "am", "PM", "pm" case-insensitively.
fn parse_am_pm(s: &str) -> Option<(bool, usize)> {
let trimmed = s.trim_start();
// Amount of whitespace skipped; can be 0
let diff = s.len() - trimmed.len();
let s = trimmed.get(0..2)?;
if s.eq_ignore_ascii_case("AM") {
return Some((false, diff + 2));
}
if s.eq_ignore_ascii_case("PM") {
return Some((true, diff + 2));
}
None
}
/// Parse a weekday name from `s`.
/// - 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 };
for (i, name) in list.iter().enumerate() {
if s.len() >= name.len() && s[0..name.len()].eq_ignore_ascii_case(name) {
return Some((i, name.len()));
}
}
None
}
/// Parse a month name from `s`.
/// - If `abbrev == true`, match short forms: "Jan".."Dec"
/// - Otherwise, match "January".."December"
/// Return (month_index, length_consumed).
fn parse_month(s: &str, abbrev: bool) -> Option<(usize, usize)> {
let list = if abbrev { &SHORT_MONTHS } else { &LONG_MONTHS };
for (i, name) in list.iter().enumerate() {
if s.len() >= name.len() && s[0..name.len()].eq_ignore_ascii_case(name) {
return Some((i, name.len()));
}
}
None
}
/// Apply a small subformat (like "%m/%d/%y" or "%Y-%m-%d") to `input`.
/// 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.
// Otherwise, we return the count.
// Convert `input` to a null-terminated buffer temporarily
let mut tmpbuf = String::with_capacity(input.len() + 1);
tmpbuf.push_str(input);
tmpbuf.push('\0');
let mut tmpfmt = String::with_capacity(subfmt.len() + 1);
tmpfmt.push_str(subfmt);
tmpfmt.push('\0');
// We need a copy of the tm, so if partial parse fails, we don't override.
let old_tm = unsafe { ptr::read(tm) }; // backup
let consumed_ptr = unsafe {
strptime(
tmpbuf.as_ptr() as *const c_char,
tmpfmt.as_ptr() as *const c_char,
tm,
)
};
if consumed_ptr.is_null() {
// revert
unsafe {
*tm = old_tm;
}
return None;
}
// consumed_ptr - tmpbuf.as_ptr() => # of bytes consumed
let diff = (consumed_ptr as usize) - (tmpbuf.as_ptr() as usize);
Some(diff)
}
#[cfg(test)]
mod tests {
use super::parse_am_pm;
#[test]
fn am_pm_parser_works() {
let am = "am";
let am_expected = Some((false, 2));
assert_eq!(am_expected, parse_am_pm(am));
let pm = "pm";
let pm_expected = Some((true, 2));
assert_eq!(pm_expected, parse_am_pm(pm));
let am_caps = "AM";
assert_eq!(am_expected, parse_am_pm(am_caps));
let pm_caps = "PM";
assert_eq!(pm_expected, parse_am_pm(pm_caps));
let am_weird = "aM";
assert_eq!(am_expected, parse_am_pm(am_weird));
let am_prefix = " \tam";
let am_prefix_expected = Some((false, 11));
assert_eq!(am_prefix_expected, parse_am_pm(am_prefix));
let pm_spaces = " pm ";
let pm_spaces_expected = Some((true, 10));
assert_eq!(pm_spaces_expected, parse_am_pm(pm_spaces));
}
}
use core::ptr; use core::ptr;
use crate::{ use crate::{
error::ResultExtPtrMut,
header::errno::ENOMEM, header::errno::ENOMEM,
platform::{self, types::*, Pal, Sys}, platform::{self, types::*, Pal, Sys},
}; };
static mut BRK: *mut c_void = ptr::null_mut(); static mut BRK: *mut c_void = ptr::null_mut();
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/brk.html>.
///
/// # Deprecation
/// The `brk()` function was marked legacy in the System Interface & Headers
/// Issue 5, and removed in Issue 6.
#[deprecated]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn brk(addr: *mut c_void) -> c_int { pub unsafe extern "C" fn brk(addr: *mut c_void) -> c_int {
BRK = Sys::brk(addr); BRK = Sys::brk(addr).or_errno_null_mut();
if BRK < addr { if BRK < addr {
platform::errno = ENOMEM; platform::ERRNO.set(ENOMEM);
return -1; return -1;
} }
0 0
} }
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/brk.html>.
///
/// # Deprecation
/// The `sbrk()` function was marked legacy in the System Interface & Headers
/// Issue 5, and removed in Issue 6.
#[deprecated]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sbrk(incr: intptr_t) -> *mut c_void { pub unsafe extern "C" fn sbrk(incr: intptr_t) -> *mut c_void {
if BRK.is_null() { if BRK.is_null() {
BRK = Sys::brk(ptr::null_mut()); BRK = Sys::brk(ptr::null_mut()).or_errno_null_mut();
} }
let old_brk = BRK; let old_brk = BRK;
...@@ -30,10 +43,10 @@ pub unsafe extern "C" fn sbrk(incr: intptr_t) -> *mut c_void { ...@@ -30,10 +43,10 @@ pub unsafe extern "C" fn sbrk(incr: intptr_t) -> *mut c_void {
if incr != 0 { if incr != 0 {
let addr = old_brk.offset(incr); let addr = old_brk.offset(incr);
BRK = Sys::brk(addr); BRK = Sys::brk(addr).or_errno_null_mut();
if BRK < addr { if BRK < addr {
platform::errno = ENOMEM; platform::ERRNO.set(ENOMEM);
return -1isize as *mut c_void; return -1isize as *mut c_void;
} }
} }
......
sys_includes = ["stddef.h", "stdint.h", "sys/types.h"] sys_includes = ["stddef.h", "stdint.h", "sys/types.h", "features.h"]
include_guard = "_UNISTD_H" include_guard = "_RELIBC_UNISTD_H"
trailer = "#include <bits/fcntl.h>\n#include <bits/unistd.h>" trailer = """
#include <bits/fcntl.h>
#include <bits/unistd.h>
"""
language = "C" language = "C"
style = "Tag" style = "Tag"
no_includes = true no_includes = true
...@@ -8,7 +11,3 @@ cpp_compat = true ...@@ -8,7 +11,3 @@ cpp_compat = true
[enum] [enum]
prefix_with_name = true prefix_with_name = true
[defines]
"target_os = linux" = "__linux__"
"target_os = redox" = "__redox__"
...@@ -4,26 +4,31 @@ use core::ptr; ...@@ -4,26 +4,31 @@ use core::ptr;
use crate::{header::getopt, platform::types::*}; use crate::{header::getopt, platform::types::*};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getopt.html>.
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
#[no_mangle] #[no_mangle]
#[linkage = "weak"] // often redefined in GNU programs #[linkage = "weak"] // often redefined in GNU programs
pub static mut optarg: *mut c_char = ptr::null_mut(); pub static mut optarg: *mut c_char = ptr::null_mut();
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getopt.html>.
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
#[no_mangle] #[no_mangle]
#[linkage = "weak"] // often redefined in GNU programs #[linkage = "weak"] // often redefined in GNU programs
pub static mut optind: c_int = 1; pub static mut opterr: c_int = 1;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getopt.html>.
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
#[no_mangle] #[no_mangle]
#[linkage = "weak"] // often redefined in GNU programs #[linkage = "weak"] // often redefined in GNU programs
pub static mut opterr: c_int = 1; pub static mut optind: c_int = 1;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getopt.html>.
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
#[no_mangle] #[no_mangle]
#[linkage = "weak"] // often redefined in GNU programs #[linkage = "weak"] // often redefined in GNU programs
pub static mut optopt: c_int = -1; pub static mut optopt: c_int = -1;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getopt.html>.
#[no_mangle] #[no_mangle]
#[linkage = "weak"] // often redefined in GNU programs #[linkage = "weak"] // often redefined in GNU programs
pub unsafe extern "C" fn getopt( pub unsafe extern "C" fn getopt(
......
use core::ptr;
use crate::{
c_str::CStr,
fs::File,
header::{
fcntl::{O_CLOEXEC, O_RDWR},
limits::PASS_MAX,
termios,
},
io::{self, Read, Write},
};
use crate::platform::types::*;
fn getpass_rs(prompt: CStr, passbuff: &mut [u8]) -> Result<*mut c_char, io::Error> {
let mut f = File::open(c"/dev/tty".into(), O_RDWR | O_CLOEXEC)?;
let mut term = termios::termios::default();
unsafe {
termios::tcgetattr(f.fd, &mut term as *mut termios::termios);
}
let old_term = term.clone();
term.c_iflag &= !(termios::IGNCR | termios::INLCR) as u32;
term.c_iflag |= termios::ICRNL as u32;
term.c_lflag &= !(termios::ECHO | termios::ISIG) as u32;
term.c_lflag |= termios::ICANON as u32;
unsafe {
termios::tcsetattr(f.fd, termios::TCSAFLUSH, &term as *const termios::termios);
}
f.write(&prompt.to_bytes())?;
f.flush()?;
let mut len = f.read(passbuff)?;
if len > 0 {
if passbuff[len - 1] == b'\n' || passbuff.len() == len {
len -= 1;
}
}
passbuff[len] = 0;
unsafe {
termios::tcsetattr(
f.fd,
termios::TCSAFLUSH,
&old_term as *const termios::termios,
);
}
f.write(b"\n")?;
f.flush()?;
Ok(passbuff.as_mut_ptr() as *mut c_char)
}
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/getpass.html>.
///
/// # Deprecation
/// The `getpass()` function was marked legacy in the Open Group System
/// Interface & Headers Issue 5, and removed in Issue 6.
#[deprecated]
#[no_mangle]
pub unsafe extern "C" fn getpass(prompt: *const c_char) -> *mut c_char {
static mut PASSBUFF: [u8; PASS_MAX] = [0; PASS_MAX];
unsafe { getpass_rs(CStr::from_ptr(prompt), &mut PASSBUFF).unwrap_or(ptr::null_mut()) }
}
//! unistd implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/unistd.h.html //! `unistd.h` implementation.
//!
use core::{convert::TryFrom, mem, ptr, slice}; //! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/unistd.h.html>.
use core::{
convert::TryFrom,
ffi::VaListImpl,
mem::{self, MaybeUninit},
ptr, slice,
};
use crate::{ use crate::{
c_str::CStr, c_str::CStr,
header::{errno, limits, stdlib::getenv, sys_ioctl, sys_time, termios, time::timespec}, error::{Errno, ResultExt},
platform::{self, types::*, Pal, Sys}, header::{
crypt::{crypt_data, crypt_r},
errno, fcntl, limits,
stdlib::getenv,
sys_ioctl, sys_resource, sys_time, sys_utsname, termios,
time::timespec,
},
platform::{self, types::*, Pal, Sys, ERRNO},
}; };
use alloc::collections::LinkedList; use alloc::collections::LinkedList;
pub use self::{brk::*, getopt::*, pathconf::*, sysconf::*}; pub use self::{brk::*, getopt::*, getpass::getpass, pathconf::*, sysconf::*};
// Inclusion of ctermid() prototype marked as obsolescent since Issue 7, cf.
// <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html>.
// cuserid() marked legacy in Issue 5.
pub use crate::header::stdio::{ctermid, cuserid};
// TODO: implement and reexport fcntl functions:
//pub use crate::header::fcntl::{faccessat, fchownat, fexecve, linkat, readlinkat, symlinkat, unlinkat};
use super::errno::{E2BIG, ENOMEM};
mod brk; mod brk;
mod getopt; mod getopt;
mod getpass;
mod pathconf; mod pathconf;
mod sysconf; mod sysconf;
...@@ -34,6 +60,8 @@ pub const STDIN_FILENO: c_int = 0; ...@@ -34,6 +60,8 @@ pub const STDIN_FILENO: c_int = 0;
pub const STDOUT_FILENO: c_int = 1; pub const STDOUT_FILENO: c_int = 1;
pub const STDERR_FILENO: c_int = 2; pub const STDERR_FILENO: c_int = 2;
pub const L_cuserid: usize = 9;
#[thread_local] #[thread_local]
pub static mut fork_hooks_static: Option<[LinkedList<extern "C" fn()>; 3]> = None; pub static mut fork_hooks_static: Option<[LinkedList<extern "C" fn()>; 3]> = None;
...@@ -46,17 +74,26 @@ unsafe fn init_fork_hooks<'a>() -> &'a mut [LinkedList<extern "C" fn()>; 3] { ...@@ -46,17 +74,26 @@ unsafe fn init_fork_hooks<'a>() -> &'a mut [LinkedList<extern "C" fn()>; 3] {
) )
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fork.html>.
// #[no_mangle]
pub unsafe extern "C" fn _Fork() -> pid_t {
unimplemented!();
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/_Exit.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn _exit(status: c_int) { pub extern "C" fn _exit(status: c_int) -> ! {
Sys::exit(status) Sys::exit(status)
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/access.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int { pub unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int {
let path = CStr::from_ptr(path); let path = CStr::from_ptr(path);
Sys::access(path, mode) Sys::access(path, mode).map(|()| 0).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alarm.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn alarm(seconds: c_uint) -> c_uint { pub extern "C" fn alarm(seconds: c_uint) -> c_uint {
let mut timer = sys_time::itimerval { let mut timer = sys_time::itimerval {
...@@ -66,90 +103,176 @@ pub extern "C" fn alarm(seconds: c_uint) -> c_uint { ...@@ -66,90 +103,176 @@ pub extern "C" fn alarm(seconds: c_uint) -> c_uint {
}, },
..Default::default() ..Default::default()
}; };
let errno_backup = unsafe { platform::errno }; let mut otimer = sys_time::itimerval::default();
let secs = if sys_time::setitimer(sys_time::ITIMER_REAL, &timer, &mut timer) < 0 {
let errno_backup = platform::ERRNO.get();
let secs = if unsafe { sys_time::setitimer(sys_time::ITIMER_REAL, &timer, &mut otimer) } < 0 {
0 0
} else { } else {
timer.it_value.tv_sec as c_uint + if timer.it_value.tv_usec > 0 { 1 } else { 0 } otimer.it_value.tv_sec as c_uint + if otimer.it_value.tv_usec > 0 { 1 } else { 0 }
}; };
unsafe { platform::ERRNO.set(errno_backup);
platform::errno = errno_backup;
}
secs secs
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/chdir.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn chdir(path: *const c_char) -> c_int { pub unsafe extern "C" fn chdir(path: *const c_char) -> c_int {
let path = CStr::from_ptr(path); let path = CStr::from_ptr(path);
Sys::chdir(path) Sys::chdir(path).map(|()| 0).or_minus_one_errno()
}
// #[no_mangle]
pub extern "C" fn chroot(path: *const c_char) -> c_int {
unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/chown.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn chown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int { pub unsafe extern "C" fn chown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int {
let path = CStr::from_ptr(path); let path = CStr::from_ptr(path);
Sys::chown(path, owner, group) Sys::chown(path, owner, group)
.map(|()| 0)
.or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/chroot.html>.
///
/// # Deprecation
/// The `chroot()` function was marked legacy in the System Interface & Headers
/// Issue 5, and removed in Issue 6.
#[deprecated]
#[no_mangle]
pub unsafe extern "C" fn chroot(path: *const c_char) -> c_int {
// TODO: Implement
platform::ERRNO.set(crate::header::errno::EPERM);
-1
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/close.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn close(fildes: c_int) -> c_int { pub extern "C" fn close(fildes: c_int) -> c_int {
Sys::close(fildes) Sys::close(fildes).map(|()| 0).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/confstr.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn confstr(name: c_int, buf: *mut c_char, len: size_t) -> size_t { pub extern "C" fn confstr(name: c_int, buf: *mut c_char, len: size_t) -> size_t {
unimplemented!(); unimplemented!();
} }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/crypt.html>.
pub extern "C" fn crypt(key: *const c_char, salt: *const c_char) -> *mut c_char { #[no_mangle]
unimplemented!(); pub unsafe extern "C" fn crypt(key: *const c_char, salt: *const c_char) -> *mut c_char {
let mut data = crypt_data::new();
crypt_r(key, salt, &mut data as *mut _)
}
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/daemon.3.html>.
#[no_mangle]
pub extern "C" fn daemon(nochdir: c_int, noclose: c_int) -> c_int {
if nochdir == 0 {
if Sys::chdir(c"/".into()).map(|()| 0).or_minus_one_errno() < 0 {
return -1;
}
}
if noclose == 0 {
let fd = Sys::open(c"/dev/null".into(), fcntl::O_RDWR, 0).or_minus_one_errno();
if fd < 0 {
return -1;
}
if dup2(fd, 0) < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0 {
close(fd);
return -1;
}
if fd > 2 {
close(fd);
}
}
match unsafe { fork() } {
0 => {}
-1 => return -1,
_ => _exit(0),
}
if setsid() < 0 {
return -1;
}
0
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn dup(fildes: c_int) -> c_int { pub extern "C" fn dup(fildes: c_int) -> c_int {
Sys::dup(fildes) Sys::dup(fildes).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn dup2(fildes: c_int, fildes2: c_int) -> c_int { pub extern "C" fn dup2(fildes: c_int, fildes2: c_int) -> c_int {
Sys::dup2(fildes, fildes2) Sys::dup2(fildes, fildes2).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn encrypt(block: [c_char; 64], edflag: c_int) { pub extern "C" fn dup3(fildes: c_int, fildes2: c_int, flag: c_int) -> c_int {
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/encrypt.html>.
///
/// # Deprecation
/// The `encrypt()` function was marked obsolescent in the Open Group Base Specifications Issue 8.
#[deprecated]
// #[no_mangle] // #[no_mangle]
// pub extern "C" fn execl(path: *const c_char, args: *const *mut c_char) -> c_int { pub extern "C" fn encrypt(block: [c_char; 64], edflag: c_int) {
// unimplemented!(); unimplemented!();
// } }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
// pub extern "C" fn execle( #[no_mangle]
// path: *const c_char, pub unsafe extern "C" fn execl(
// args: *const *mut c_char, path: *const c_char,
// envp: *const *mut c_char, arg0: *const c_char,
// ) -> c_int { mut __valist: ...
// unimplemented!(); ) -> c_int {
// } with_argv(__valist, arg0, |args, _remaining_va| {
execv(path, args.as_ptr().cast())
})
}
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
// pub extern "C" fn execlp(file: *const c_char, args: *const *mut c_char) -> c_int { #[no_mangle]
// unimplemented!(); pub unsafe extern "C" fn execle(
// } path: *const c_char,
arg0: *const c_char,
mut __valist: ...
) -> c_int {
with_argv(__valist, arg0, |args, mut remaining_va| {
let envp = remaining_va.arg::<*const *mut c_char>();
execve(path, args.as_ptr().cast(), envp)
})
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
#[no_mangle]
pub unsafe extern "C" fn execlp(
file: *const c_char,
arg0: *const c_char,
mut __valist: ...
) -> c_int {
with_argv(__valist, arg0, |args, _remaining_va| {
execvp(file, args.as_ptr().cast())
})
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn execv(path: *const c_char, argv: *const *mut c_char) -> c_int { pub unsafe extern "C" fn execv(path: *const c_char, argv: *const *mut c_char) -> c_int {
execve(path, argv, platform::environ) execve(path, argv, platform::environ)
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn execve( pub unsafe extern "C" fn execve(
path: *const c_char, path: *const c_char,
...@@ -158,6 +281,8 @@ pub unsafe extern "C" fn execve( ...@@ -158,6 +281,8 @@ pub unsafe extern "C" fn execve(
) -> c_int { ) -> c_int {
let path = CStr::from_ptr(path); let path = CStr::from_ptr(path);
Sys::execve(path, argv, envp) Sys::execve(path, argv, envp)
.map(|()| unreachable!())
.or_minus_one_errno()
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
...@@ -166,6 +291,7 @@ const PATH_SEPARATOR: u8 = b':'; ...@@ -166,6 +291,7 @@ const PATH_SEPARATOR: u8 = b':';
#[cfg(target_os = "redox")] #[cfg(target_os = "redox")]
const PATH_SEPARATOR: u8 = b';'; const PATH_SEPARATOR: u8 = b';';
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) -> c_int { pub unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) -> c_int {
let file = CStr::from_ptr(file); let file = CStr::from_ptr(file);
...@@ -177,52 +303,61 @@ pub unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) - ...@@ -177,52 +303,61 @@ pub unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) -
} else { } else {
let mut error = errno::ENOENT; let mut error = errno::ENOENT;
let path_env = getenv(c_str!("PATH\0").as_ptr()); let path_env = getenv(c"PATH".as_ptr());
if !path_env.is_null() { if !path_env.is_null() {
let path_env = CStr::from_ptr(path_env); let path_env = CStr::from_ptr(path_env);
for path in path_env.to_bytes().split(|&b| b == PATH_SEPARATOR) { for path in path_env.to_bytes().split(|&b| b == PATH_SEPARATOR) {
let mut program = path.to_vec(); let file = file.to_bytes();
let length = file.len() + path.len() + 2;
let mut program = alloc::vec::Vec::with_capacity(length);
program.extend_from_slice(path);
program.push(b'/'); program.push(b'/');
program.extend_from_slice(file.to_bytes()); program.extend_from_slice(file);
program.push(b'\0'); program.push(b'\0');
let program_c = CStr::from_bytes_with_nul(&program).unwrap(); let program_c = CStr::from_bytes_with_nul(&program).unwrap();
execv(program_c.as_ptr(), argv); execv(program_c.as_ptr(), argv);
match platform::errno { match platform::ERRNO.get() {
errno::ENOENT => (), errno::ENOENT => (),
other => error = other, other => error = other,
} }
} }
} }
platform::errno = error; platform::ERRNO.set(error);
-1 -1
} }
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchdir.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> c_int { pub extern "C" fn fchdir(fildes: c_int) -> c_int {
Sys::fchown(fildes, owner, group) Sys::fchdir(fildes).map(|()| 0).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchown.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn fchdir(fildes: c_int) -> c_int { pub extern "C" fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> c_int {
Sys::fchdir(fildes) Sys::fchown(fildes, owner, group)
.map(|()| 0)
.or_minus_one_errno()
} }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdatasync.html>.
#[no_mangle]
pub extern "C" fn fdatasync(fildes: c_int) -> c_int { pub extern "C" fn fdatasync(fildes: c_int) -> c_int {
unimplemented!(); Sys::fdatasync(fildes).map(|()| 0).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fork.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn fork() -> pid_t { pub unsafe extern "C" fn fork() -> pid_t {
let fork_hooks = unsafe { init_fork_hooks() }; let fork_hooks = init_fork_hooks();
for prepare in &fork_hooks[0] { for prepare in &fork_hooks[0] {
prepare(); prepare();
} }
let pid = Sys::fork(); let pid = Sys::fork().or_minus_one_errno();
if pid == 0 { if pid == 0 {
for child in &fork_hooks[2] { for child in &fork_hooks[2] {
child(); child();
...@@ -235,18 +370,23 @@ pub extern "C" fn fork() -> pid_t { ...@@ -235,18 +370,23 @@ pub extern "C" fn fork() -> pid_t {
pid pid
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fsync.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn fsync(fildes: c_int) -> c_int { pub extern "C" fn fsync(fildes: c_int) -> c_int {
Sys::fsync(fildes) Sys::fsync(fildes).map(|()| 0).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ftruncate.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn ftruncate(fildes: c_int, length: off_t) -> c_int { pub extern "C" fn ftruncate(fildes: c_int, length: off_t) -> c_int {
Sys::ftruncate(fildes, length) Sys::ftruncate(fildes, length)
.map(|()| 0)
.or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getcwd.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char { pub unsafe extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char {
let alloc = buf.is_null(); let alloc = buf.is_null();
let mut stack_buf = [0; limits::PATH_MAX]; let mut stack_buf = [0; limits::PATH_MAX];
if alloc { if alloc {
...@@ -254,10 +394,13 @@ pub extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char ...@@ -254,10 +394,13 @@ pub extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char
size = stack_buf.len(); size = stack_buf.len();
} }
let ret = Sys::getcwd(buf, size); let ret = match Sys::getcwd(buf, size) {
if ret.is_null() { Ok(()) => buf,
return ptr::null_mut(); Err(Errno(errno)) => {
} ERRNO.set(errno);
return ptr::null_mut();
}
};
if alloc { if alloc {
let len = stack_buf let len = stack_buf
...@@ -277,45 +420,80 @@ pub extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char ...@@ -277,45 +420,80 @@ pub extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char
} }
} }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/getdtablesize.html>.
///
/// # Deprecation
/// The `getdtablesize()` function was marked legacy in the System Interface &
/// Headers Issue 5, and removed in Issue 6.
#[deprecated]
#[no_mangle]
pub extern "C" fn getdtablesize() -> c_int { pub extern "C" fn getdtablesize() -> c_int {
unimplemented!(); let mut lim = mem::MaybeUninit::<sys_resource::rlimit>::uninit();
let r = unsafe {
sys_resource::getrlimit(
sys_resource::RLIMIT_NOFILE as c_int,
lim.as_mut_ptr() as *mut sys_resource::rlimit,
)
};
if r == 0 {
let cur = unsafe { lim.assume_init() }.rlim_cur;
match cur {
c if c < i32::MAX as u64 => c as i32,
_ => i32::MAX,
};
}
-1
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getegid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn getegid() -> gid_t { pub extern "C" fn getegid() -> gid_t {
Sys::getegid() Sys::getegid()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html>.
// #[no_mangle]
pub extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int {
unimplemented!();
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/geteuid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn geteuid() -> uid_t { pub extern "C" fn geteuid() -> uid_t {
Sys::geteuid() Sys::geteuid()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn getgid() -> gid_t { pub extern "C" fn getgid() -> gid_t {
Sys::getgid() Sys::getgid()
} }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgroups.html>.
pub extern "C" fn getgroups(gidsetsize: c_int, grouplist: *mut gid_t) -> c_int { #[no_mangle]
unimplemented!(); pub unsafe extern "C" fn getgroups(size: c_int, list: *mut gid_t) -> c_int {
Sys::getgroups(size, list).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/gethostid.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn gethostid() -> c_long { pub extern "C" fn gethostid() -> c_long {
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/gethostname.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) -> c_int { pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) -> c_int {
let mut uts = mem::uninitialized(); let mut uts = mem::MaybeUninit::<sys_utsname::utsname>::uninit();
let err = Sys::uname(&mut uts); // TODO
let err = Sys::uname(uts.as_mut_ptr())
.map(|()| 0)
.or_minus_one_errno();
if err < 0 { if err < 0 {
mem::forget(uts); mem::forget(uts);
return err; return err;
} }
for c in uts.nodename.iter() { for c in uts.assume_init().nodename.iter() {
if len == 0 { if len == 0 {
break; break;
} }
...@@ -333,6 +511,7 @@ pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) -> ...@@ -333,6 +511,7 @@ pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) ->
0 0
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getlogin.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn getlogin() -> *mut c_char { pub unsafe extern "C" fn getlogin() -> *mut c_char {
static mut LOGIN: [c_char; 256] = [0; 256]; static mut LOGIN: [c_char; 256] = [0; 256];
...@@ -343,72 +522,88 @@ pub unsafe extern "C" fn getlogin() -> *mut c_char { ...@@ -343,72 +522,88 @@ pub unsafe extern "C" fn getlogin() -> *mut c_char {
} }
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getlogin.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn getlogin_r(name: *mut c_char, namesize: size_t) -> c_int { pub extern "C" fn getlogin_r(name: *mut c_char, namesize: size_t) -> c_int {
//TODO: Determine correct getlogin result on Redox //TODO: Determine correct getlogin result on Redox
unsafe { platform::errno = errno::ENOENT }; platform::ERRNO.set(errno::ENOENT);
-1 -1
} }
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/getpagesize.html>.
///
/// # Deprecation
/// The `getpagesize()` function was marked legacy in the System Interface &
/// Headers Issue 5, and removed in Issue 6.
#[deprecated]
#[no_mangle] #[no_mangle]
pub extern "C" fn getpagesize() -> c_int { pub extern "C" fn getpagesize() -> c_int {
match c_int::try_from(sysconf(_SC_PAGESIZE)) { // Panic if we can't uphold the required behavior (no errors are specified for this function)
Ok(page_size) => page_size, Sys::getpagesize()
Err(_) => { .try_into()
/* Behavior not specified by POSIX for this case. The -1 .expect("page size not representable as type `int`")
* value mimics sysconf()'s behavior, though.
*
* As specified for the limits.h header, the minimum
* acceptable value for {PAGESIZE} is 1. The -1 value thus
* cannot be mistaken for an acceptable value.
*
* POSIX does not specify any possible errors for this
* function, hence no errno setting. */
-1
}
}
}
// #[no_mangle]
pub extern "C" fn getpass(prompt: *const c_char) -> *mut c_char {
unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpgid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn getpgid(pid: pid_t) -> pid_t { pub extern "C" fn getpgid(pid: pid_t) -> pid_t {
Sys::getpgid(pid) Sys::getpgid(pid).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpgrp.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn getpgrp() -> pid_t { pub extern "C" fn getpgrp() -> pid_t {
Sys::getpgid(Sys::getpid()) Sys::getpgid(Sys::getpid()).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn getpid() -> pid_t { pub extern "C" fn getpid() -> pid_t {
Sys::getpid() Sys::getpid()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getppid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn getppid() -> pid_t { pub extern "C" fn getppid() -> pid_t {
Sys::getppid() Sys::getppid()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getresgid.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn getsid(pid: pid_t) -> pid_t { pub extern "C" fn getresgid(rgid: *mut gid_t, egid: *mut gid_t, sgid: *mut gid_t) -> c_int {
unimplemented!();
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getresuid.html>.
// #[no_mangle]
pub extern "C" fn getresuid(ruid: *mut uid_t, euid: *mut uid_t, suid: *mut uid_t) -> c_int {
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getsid.html>.
#[no_mangle]
pub extern "C" fn getsid(pid: pid_t) -> pid_t {
Sys::getsid(pid).or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getuid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn getuid() -> uid_t { pub extern "C" fn getuid() -> uid_t {
Sys::getuid() Sys::getuid()
} }
/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/getwd.html>.
///
/// # Deprecation
/// The `getwd()` function was marked legacy in the Open Group Base
/// Specifications Issue 6, and removed in Issue 7.
#[deprecated]
#[no_mangle] #[no_mangle]
pub extern "C" fn getwd(path_name: *mut c_char) -> *mut c_char { pub unsafe extern "C" fn getwd(path_name: *mut c_char) -> *mut c_char {
getcwd(path_name, limits::PATH_MAX) unsafe { getcwd(path_name, limits::PATH_MAX) }
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isatty.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn isatty(fd: c_int) -> c_int { pub extern "C" fn isatty(fd: c_int) -> c_int {
let mut t = termios::termios::default(); let mut t = termios::termios::default();
...@@ -419,69 +614,121 @@ pub extern "C" fn isatty(fd: c_int) -> c_int { ...@@ -419,69 +614,121 @@ pub extern "C" fn isatty(fd: c_int) -> c_int {
} }
} }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lchown.html>.
pub extern "C" fn lchown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int { #[no_mangle]
unimplemented!(); pub unsafe extern "C" fn lchown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int {
let path = CStr::from_ptr(path);
Sys::lchown(path, owner, group)
.map(|()| 0)
.or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/link.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn link(path1: *const c_char, path2: *const c_char) -> c_int { pub unsafe extern "C" fn link(path1: *const c_char, path2: *const c_char) -> c_int {
let path1 = CStr::from_ptr(path1); let path1 = CStr::from_ptr(path1);
let path2 = CStr::from_ptr(path2); let path2 = CStr::from_ptr(path2);
Sys::link(path1, path2) Sys::link(path1, path2).map(|()| 0).or_minus_one_errno()
} }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lockf.html>.
pub extern "C" fn lockf(fildes: c_int, function: c_int, size: off_t) -> c_int { #[no_mangle]
unimplemented!(); pub unsafe extern "C" fn lockf(fildes: c_int, function: c_int, size: off_t) -> c_int {
let mut fl = fcntl::flock {
l_type: fcntl::F_WRLCK as c_short,
l_whence: SEEK_CUR as c_short,
l_start: 0,
l_len: size,
l_pid: -1,
};
match function {
fcntl::F_TEST => {
fl.l_type = fcntl::F_RDLCK as c_short;
if fcntl::fcntl(fildes, fcntl::F_GETLK, &mut fl as *mut _ as c_ulonglong) < 0 {
return -1;
}
if fl.l_type == fcntl::F_UNLCK as c_short || fl.l_pid == getpid() {
return 0;
}
platform::ERRNO.set(errno::EACCES);
return -1;
}
fcntl::F_ULOCK => {
fl.l_type = fcntl::F_UNLCK as c_short;
return fcntl::fcntl(fildes, fcntl::F_SETLK, &mut fl as *mut _ as c_ulonglong);
}
fcntl::F_TLOCK => {
return fcntl::fcntl(fildes, fcntl::F_SETLK, &mut fl as *mut _ as c_ulonglong);
}
fcntl::F_LOCK => {
return fcntl::fcntl(fildes, fcntl::F_SETLKW, &mut fl as *mut _ as c_ulonglong);
}
_ => {
platform::ERRNO.set(errno::EINVAL);
return -1;
}
};
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lseek.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t { pub extern "C" fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t {
Sys::lseek(fildes, offset, whence) Sys::lseek(fildes, offset, whence).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/nice.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn nice(incr: c_int) -> c_int { pub extern "C" fn nice(incr: c_int) -> c_int {
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pause.html>.
// #[no_mangle] // #[no_mangle]
pub extern "C" fn pause() -> c_int { pub extern "C" fn pause() -> c_int {
unimplemented!(); unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pipe.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn pipe(fildes: *mut c_int) -> c_int { pub unsafe extern "C" fn pipe(fildes: *mut c_int) -> c_int {
pipe2(fildes, 0) pipe2(fildes, 0)
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pipe.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn pipe2(fildes: *mut c_int, flags: c_int) -> c_int { pub unsafe extern "C" fn pipe2(fildes: *mut c_int, flags: c_int) -> c_int {
Sys::pipe2(slice::from_raw_parts_mut(fildes, 2), flags) Sys::pipe2(slice::from_raw_parts_mut(fildes, 2), flags)
.map(|()| 0)
.or_minus_one_errno()
} }
#[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/close.html>.
pub extern "C" fn pread(fildes: c_int, buf: *mut c_void, nbyte: size_t, offset: off_t) -> ssize_t { // #[no_mangle]
//TODO: better pread using system calls pub extern "C" fn posix_close(fildes: c_int, flag: c_int) -> c_int {
unimplemented!();
let previous = lseek(fildes, offset, SEEK_SET); }
if previous == -1 {
return -1;
}
let res = read(fildes, buf, nbyte);
if res < 0 {
return res;
}
if lseek(fildes, previous, SEEK_SET) == -1 {
return -1;
}
res /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/read.html>.
#[no_mangle]
pub unsafe extern "C" fn pread(
fildes: c_int,
buf: *mut c_void,
nbyte: size_t,
offset: off_t,
) -> ssize_t {
Sys::pread(
fildes,
slice::from_raw_parts_mut(buf.cast::<u8>(), nbyte),
offset,
)
.map(|read| read as ssize_t)
.or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_atfork.html>.
///
/// TODO: specified in `pthread.h` in modern POSIX
#[no_mangle] #[no_mangle]
pub extern "C" fn pthread_atfork( pub extern "C" fn pthread_atfork(
prepare: Option<extern "C" fn()>, prepare: Option<extern "C" fn()>,
...@@ -501,37 +748,31 @@ pub extern "C" fn pthread_atfork( ...@@ -501,37 +748,31 @@ pub extern "C" fn pthread_atfork(
0 0
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/write.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn pwrite( pub unsafe extern "C" fn pwrite(
fildes: c_int, fildes: c_int,
buf: *const c_void, buf: *const c_void,
nbyte: size_t, nbyte: size_t,
offset: off_t, offset: off_t,
) -> ssize_t { ) -> ssize_t {
//TODO: better pwrite using system calls Sys::pwrite(
fildes,
let previous = lseek(fildes, offset, SEEK_SET); slice::from_raw_parts(buf.cast::<u8>(), nbyte),
if previous == -1 { offset,
return -1; )
} .map(|read| read as ssize_t)
.or_minus_one_errno()
let res = write(fildes, buf, nbyte);
if res < 0 {
return res;
}
if lseek(fildes, previous, SEEK_SET) == -1 {
return -1;
}
res
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/read.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn read(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssize_t { pub unsafe extern "C" fn read(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssize_t {
let buf = unsafe { slice::from_raw_parts_mut(buf as *mut u8, nbyte as usize) }; let buf = unsafe { slice::from_raw_parts_mut(buf as *mut u8, nbyte as usize) };
trace_expr!( trace_expr!(
Sys::read(fildes, buf), Sys::read(fildes, buf)
.map(|read| read as ssize_t)
.or_minus_one_errno(),
"read({}, {:p}, {})", "read({}, {:p}, {})",
fildes, fildes,
buf, buf,
...@@ -539,6 +780,7 @@ pub extern "C" fn read(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssiz ...@@ -539,6 +780,7 @@ pub extern "C" fn read(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssiz
) )
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/readlink.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn readlink( pub unsafe extern "C" fn readlink(
path: *const c_char, path: *const c_char,
...@@ -548,60 +790,138 @@ pub unsafe extern "C" fn readlink( ...@@ -548,60 +790,138 @@ pub unsafe extern "C" fn readlink(
let path = CStr::from_ptr(path); let path = CStr::from_ptr(path);
let buf = slice::from_raw_parts_mut(buf as *mut u8, bufsize as usize); let buf = slice::from_raw_parts_mut(buf as *mut u8, bufsize as usize);
Sys::readlink(path, buf) Sys::readlink(path, buf)
.map(|read| read as ssize_t)
.or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rmdir.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn rmdir(path: *const c_char) -> c_int { pub unsafe extern "C" fn rmdir(path: *const c_char) -> c_int {
let path = CStr::from_ptr(path); let path = CStr::from_ptr(path);
Sys::rmdir(path) Sys::rmdir(path).map(|()| 0).or_minus_one_errno()
}
#[no_mangle]
pub unsafe extern "C" fn set_default_scheme(scheme: *const c_char) -> c_int {
let scheme = CStr::from_ptr(scheme);
Sys::set_default_scheme(scheme)
.map(|_| 0)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setegid.html>.
// #[no_mangle]
pub extern "C" fn setegid(gid: gid_t) -> c_int {
unimplemented!();
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/seteuid.html>.
// #[no_mangle]
pub extern "C" fn seteuid(uid: uid_t) -> c_int {
unimplemented!();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setgid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn setgid(gid: gid_t) -> c_int { pub extern "C" fn setgid(gid: gid_t) -> c_int {
Sys::setregid(gid, gid) Sys::setresgid(gid, gid, -1)
.map(|()| 0)
.or_minus_one_errno()
} }
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man2/setgroups.2.html>.
///
/// TODO: specified in `grp.h`?
#[no_mangle]
pub unsafe extern "C" fn setgroups(size: size_t, list: *const gid_t) -> c_int {
Sys::setgroups(size, list).map(|()| 0).or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setpgid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int { pub extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
Sys::setpgid(pid, pgid) Sys::setpgid(pid, pgid).map(|()| 0).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgrp.html>.
///
/// # Deprecation
/// The `setpgrp()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 7, and removed in Issue 8.
#[deprecated]
#[no_mangle] #[no_mangle]
pub extern "C" fn setpgrp() -> pid_t { pub extern "C" fn setpgrp() -> pid_t {
setpgid(0, 0) setpgid(0, 0)
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setregid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn setregid(rgid: gid_t, egid: gid_t) -> c_int { pub extern "C" fn setregid(rgid: gid_t, egid: gid_t) -> c_int {
Sys::setregid(rgid, egid) Sys::setresgid(rgid, egid, -1)
.map(|()| 0)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setresgid.html>.
#[no_mangle]
pub extern "C" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> c_int {
Sys::setresgid(rgid, egid, sgid)
.map(|()| 0)
.or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setresuid.html>.
#[no_mangle]
pub extern "C" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> c_int {
Sys::setresuid(ruid, euid, suid)
.map(|()| 0)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setreuid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn setreuid(ruid: uid_t, euid: uid_t) -> c_int { pub extern "C" fn setreuid(ruid: uid_t, euid: uid_t) -> c_int {
Sys::setreuid(ruid, euid) Sys::setresuid(ruid, euid, -1)
.map(|()| 0)
.or_minus_one_errno()
} }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setsid.html>.
#[no_mangle]
pub extern "C" fn setsid() -> pid_t { pub extern "C" fn setsid() -> pid_t {
unimplemented!(); Sys::setsid().map(|()| 0).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setuid.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn setuid(uid: uid_t) -> c_int { pub extern "C" fn setuid(uid: uid_t) -> c_int {
Sys::setreuid(uid, uid) Sys::setresuid(uid, uid, -1)
.map(|()| 0)
.or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sleep.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn sleep(seconds: c_uint) -> c_uint { pub extern "C" fn sleep(seconds: c_uint) -> c_uint {
let rqtp = timespec { let rqtp = timespec {
tv_sec: seconds as i64, tv_sec: seconds as time_t,
tv_nsec: 0, tv_nsec: 0,
}; };
let rmtp = ptr::null_mut(); let mut rmtp = timespec {
Sys::nanosleep(&rqtp, rmtp); tv_sec: 0,
0 tv_nsec: 0,
};
// If sleep() returns because the requested time has elapsed, the value returned shall be 0.
// If sleep() returns due to delivery of a signal, the return value shall be the "unslept" amount
// (the requested time minus the time actually slept) in seconds.
match unsafe { Sys::nanosleep(&rqtp, &mut rmtp) } {
Err(Errno(EINTR)) => rmtp.tv_sec as c_uint,
r => 0,
}
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/swab.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn swab(src: *const c_void, dest: *mut c_void, nbytes: ssize_t) { pub extern "C" fn swab(src: *const c_void, dest: *mut c_void, nbytes: ssize_t) {
if nbytes <= 0 { if nbytes <= 0 {
...@@ -618,18 +938,21 @@ pub extern "C" fn swab(src: *const c_void, dest: *mut c_void, nbytes: ssize_t) { ...@@ -618,18 +938,21 @@ pub extern "C" fn swab(src: *const c_void, dest: *mut c_void, nbytes: ssize_t) {
} }
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/symlink.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn symlink(path1: *const c_char, path2: *const c_char) -> c_int { pub unsafe extern "C" fn symlink(path1: *const c_char, path2: *const c_char) -> c_int {
let path1 = CStr::from_ptr(path1); let path1 = CStr::from_ptr(path1);
let path2 = CStr::from_ptr(path2); let path2 = CStr::from_ptr(path2);
Sys::symlink(path1, path2) Sys::symlink(path1, path2).map(|()| 0).or_minus_one_errno()
} }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sync.html>.
#[no_mangle]
pub extern "C" fn sync() { pub extern "C" fn sync() {
unimplemented!(); Sys::sync();
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcgetpgrp.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn tcgetpgrp(fd: c_int) -> pid_t { pub extern "C" fn tcgetpgrp(fd: c_int) -> pid_t {
let mut pgrp = 0; let mut pgrp = 0;
...@@ -639,6 +962,7 @@ pub extern "C" fn tcgetpgrp(fd: c_int) -> pid_t { ...@@ -639,6 +962,7 @@ pub extern "C" fn tcgetpgrp(fd: c_int) -> pid_t {
pgrp pgrp
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcsetpgrp.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn tcsetpgrp(fd: c_int, pgrp: pid_t) -> c_int { pub extern "C" fn tcsetpgrp(fd: c_int, pgrp: pid_t) -> c_int {
if unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCSPGRP, &pgrp as *const pid_t as _) } < 0 { if unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCSPGRP, &pgrp as *const pid_t as _) } < 0 {
...@@ -647,11 +971,24 @@ pub extern "C" fn tcsetpgrp(fd: c_int, pgrp: pid_t) -> c_int { ...@@ -647,11 +971,24 @@ pub extern "C" fn tcsetpgrp(fd: c_int, pgrp: pid_t) -> c_int {
pgrp pgrp
} }
// #[no_mangle] /// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/truncate.html>.
pub extern "C" fn truncate(path: *const c_char, length: off_t) -> c_int { #[no_mangle]
unimplemented!(); pub unsafe extern "C" fn truncate(path: *const c_char, length: off_t) -> c_int {
let file = unsafe { CStr::from_ptr(path) };
// TODO: Rustify
let fd = Sys::open(file, fcntl::O_WRONLY, 0).or_minus_one_errno();
if fd < 0 {
return -1;
}
let res = ftruncate(fd, length);
Sys::close(fd);
res
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ttyname.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn ttyname(fildes: c_int) -> *mut c_char { pub unsafe extern "C" fn ttyname(fildes: c_int) -> *mut c_char {
static mut TTYNAME: [c_char; 4096] = [0; 4096]; static mut TTYNAME: [c_char; 4096] = [0; 4096];
...@@ -662,6 +999,7 @@ pub unsafe extern "C" fn ttyname(fildes: c_int) -> *mut c_char { ...@@ -662,6 +999,7 @@ pub unsafe extern "C" fn ttyname(fildes: c_int) -> *mut c_char {
} }
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ttyname.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn ttyname_r(fildes: c_int, name: *mut c_char, namesize: size_t) -> c_int { pub extern "C" fn ttyname_r(fildes: c_int, name: *mut c_char, namesize: size_t) -> c_int {
let name = unsafe { slice::from_raw_parts_mut(name as *mut u8, namesize) }; let name = unsafe { slice::from_raw_parts_mut(name as *mut u8, namesize) };
...@@ -669,15 +1007,23 @@ pub extern "C" fn ttyname_r(fildes: c_int, name: *mut c_char, namesize: size_t) ...@@ -669,15 +1007,23 @@ pub extern "C" fn ttyname_r(fildes: c_int, name: *mut c_char, namesize: size_t)
return errno::ERANGE; return errno::ERANGE;
} }
let len = Sys::fpath(fildes, &mut name[..namesize - 1]); let len = Sys::fpath(fildes, &mut name[..namesize - 1])
.map(|read| read as ssize_t)
.or_minus_one_errno();
if len < 0 { if len < 0 {
return unsafe { -platform::errno }; return -platform::ERRNO.get();
} }
name[len as usize] = 0; name[len as usize] = 0;
0 0
} }
/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/ualarm.html>.
///
/// # Deprecation
/// The `ualarm()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 6, and removed in Issue 7.
#[deprecated]
#[no_mangle] #[no_mangle]
pub extern "C" fn ualarm(usecs: useconds_t, interval: useconds_t) -> useconds_t { pub extern "C" fn ualarm(usecs: useconds_t, interval: useconds_t) -> useconds_t {
let mut timer = sys_time::itimerval { let mut timer = sys_time::itimerval {
...@@ -690,42 +1036,103 @@ pub extern "C" fn ualarm(usecs: useconds_t, interval: useconds_t) -> useconds_t ...@@ -690,42 +1036,103 @@ pub extern "C" fn ualarm(usecs: useconds_t, interval: useconds_t) -> useconds_t
tv_usec: interval as suseconds_t, tv_usec: interval as suseconds_t,
}, },
}; };
let errno_backup = unsafe { platform::errno }; let errno_backup = platform::ERRNO.get();
let ret = if sys_time::setitimer(sys_time::ITIMER_REAL, &timer, &mut timer) < 0 { let ret = if unsafe { sys_time::setitimer(sys_time::ITIMER_REAL, &timer, &mut timer) } < 0 {
0 0
} else { } else {
timer.it_value.tv_sec as useconds_t * 1_000_000 + timer.it_value.tv_usec as useconds_t timer.it_value.tv_sec as useconds_t * 1_000_000 + timer.it_value.tv_usec as useconds_t
}; };
unsafe { platform::ERRNO.set(errno_backup);
platform::errno = errno_backup;
}
ret ret
} }
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/unlink.html>.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn unlink(path: *const c_char) -> c_int { pub unsafe extern "C" fn unlink(path: *const c_char) -> c_int {
let path = CStr::from_ptr(path); let path = CStr::from_ptr(path);
Sys::unlink(path) Sys::unlink(path).map(|()| 0).or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/usleep.html>.
///
/// # Deprecation
/// The `usleep()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 6, and removed in Issue 7.
#[deprecated]
#[no_mangle] #[no_mangle]
pub extern "C" fn usleep(useconds: useconds_t) -> c_int { pub extern "C" fn usleep(useconds: useconds_t) -> c_int {
let rqtp = timespec { let rqtp = timespec {
tv_sec: (useconds / 1_000_000) as i64, tv_sec: (useconds / 1_000_000) as time_t,
tv_nsec: ((useconds % 1_000_000) * 1000) as i64, tv_nsec: ((useconds % 1_000_000) * 1000) as c_long,
}; };
let rmtp = ptr::null_mut(); let rmtp = ptr::null_mut();
Sys::nanosleep(&rqtp, rmtp) unsafe { Sys::nanosleep(&rqtp, rmtp) }
.map(|()| 0)
.or_minus_one_errno()
} }
/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/vfork.html>.
///
/// # Deprecation
/// The `vfork()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 6, and removed in Issue 7.
#[deprecated]
// #[no_mangle] // #[no_mangle]
pub extern "C" fn vfork() -> pid_t { pub extern "C" fn vfork() -> pid_t {
unimplemented!(); unimplemented!();
} }
unsafe fn with_argv(
mut va: VaListImpl,
arg0: *const c_char,
f: impl FnOnce(&[*const c_char], VaListImpl) -> c_int,
) -> c_int {
let argc = 1 + va.with_copy(|mut copy| {
core::iter::from_fn(|| Some(copy.arg::<*const c_char>()))
.position(|p| p.is_null())
.unwrap()
});
let mut stack: [MaybeUninit<*const c_char>; 32] = [MaybeUninit::uninit(); 32];
let out = if argc < 32 {
stack.as_mut_slice()
} else if argc < 4096 {
// TODO: Use ARG_MAX, not this hardcoded constant
let ptr = crate::header::stdlib::malloc(argc * mem::size_of::<*const c_char>());
if ptr.is_null() {
platform::ERRNO.set(ENOMEM);
return -1;
}
slice::from_raw_parts_mut(ptr.cast::<MaybeUninit<*const c_char>>(), argc)
} else {
platform::ERRNO.set(E2BIG);
return -1;
};
out[0].write(arg0);
for i in 1..argc {
out[i].write(va.arg::<*const c_char>());
}
out[argc].write(core::ptr::null());
// NULL
va.arg::<*const c_char>();
f(MaybeUninit::slice_assume_init_ref(&*out), va);
// f only returns if it fails
if argc >= 32 {
crate::header::stdlib::free(out.as_mut_ptr().cast());
}
-1
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/write.html>.
#[no_mangle] #[no_mangle]
pub extern "C" fn write(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssize_t { pub unsafe extern "C" fn write(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssize_t {
let buf = unsafe { slice::from_raw_parts(buf as *const u8, nbyte as usize) }; let buf = slice::from_raw_parts(buf as *const u8, nbyte as usize);
Sys::write(fildes, buf) Sys::write(fildes, buf)
.map(|bytes| bytes as ssize_t)
.or_minus_one_errno()
} }