Merge branch 'termios-func-doc' into 'master'

document termios functions

See merge request redox-os/relibc!1526
This commit is contained in:
Jeremy Soller
2026-07-06 05:45:24 -06:00
3 changed files with 199 additions and 72 deletions
+7
View File
@@ -21,6 +21,13 @@ pub struct sgttyb {
sg_flags: c_ushort,
}
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man2/ioctl.2.html>.
///
/// Manipulates the underlying device parameters of special files.
///
/// Upon success, usually returns `0`. A few operations use the return value
/// as an output parameter and returns a nonnegative value on success. Upon
/// failure, `-1` is returned and errno set to indicate the error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) -> c_int {
// TODO: Somehow support varargs to syscall??
+184 -70
View File
@@ -91,67 +91,122 @@ pub struct termios {
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcgetattr.html>.
///
/// Gets the parameters associated with the terminal referred to by `fildes`
/// and stores them in the termios structure referenced by `termios_p`.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tcgetattr(fd: c_int, out: *mut termios) -> c_int {
unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCGETS, out.cast::<c_void>()) }
pub unsafe extern "C" fn tcgetattr(fildes: c_int, termios_p: *mut termios) -> c_int {
unsafe { sys_ioctl::ioctl(fildes, sys_ioctl::TCGETS, termios_p.cast::<c_void>()) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcsetattr.html>.
///
/// Sets the parameters associated with the terminal referred to by `fildes`
/// from the termios structure referred to by `termios_p`.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tcsetattr(fd: c_int, act: c_int, value: *const termios) -> c_int {
if !(0..=2).contains(&act) {
platform::ERRNO.set(errno::EINVAL);
return -1;
pub unsafe extern "C" fn tcsetattr(
fildes: c_int,
optional_actions: c_int,
termios_p: *const termios,
) -> c_int {
match optional_actions {
TCSANOW | TCSADRAIN | TCSAFLUSH => {
// This is safe because ioctl shouldn't modify the value
unsafe {
sys_ioctl::ioctl(
fildes,
sys_ioctl::TCSETS + optional_actions as c_ulong,
termios_p as *mut c_void,
)
}
}
_ => {
platform::ERRNO.set(errno::EINVAL);
-1
}
}
// This is safe because ioctl shouldn't modify the value
unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCSETS + act as c_ulong, value as *mut c_void) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcgetsid.html>.
///
/// Obtains the process group ID of the session for which the terminal
/// specified by `fildes` is the controlling terminal.
///
/// Upon success, returns the process group ID of the session associated with
/// the terminal. Upon failure, returns `-1` and sets errno to indicate the
/// error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tcgetsid(fd: c_int) -> pid_t {
pub unsafe extern "C" fn tcgetsid(fildes: c_int) -> pid_t {
let mut sid = 0;
if unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCGSID, (&raw mut sid).cast::<c_void>()) } < 0 {
if unsafe { sys_ioctl::ioctl(fildes, sys_ioctl::TIOCGSID, (&raw mut sid).cast::<c_void>()) } < 0
{
return -1;
}
sid
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cfgetispeed.html>.
#[cfg(target_os = "linux")]
///
/// Extracts the input baud rate from the termios structure reference to by
/// `termios_p`.
///
/// Returns a value representing the input baud rate.
///
/// # Implementation
/// Infallible. Always returns a value.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cfgetispeed(termios_p: *const termios) -> speed_t {
unsafe { (*termios_p).__c_ispeed }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cfgetispeed.html>.
#[cfg(target_os = "redox")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cfgetispeed(termios_p: *const termios) -> speed_t {
//TODO
0
#[cfg(target_os = "linux")]
{
unsafe { (*termios_p).__c_ispeed }
}
#[cfg(target_os = "redox")]
{
//TODO
0
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cfgetospeed.html>.
#[cfg(target_os = "linux")]
///
/// Extracts the output baud rate from the termios structure reference to by
/// `termios_p`.
///
/// Returns a value representing the output baud rate.
///
/// # Implementation
/// Infallible. Always returns a value.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cfgetospeed(termios_p: *const termios) -> speed_t {
unsafe { (*termios_p).__c_ospeed }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cfgetospeed.html>.
#[cfg(target_os = "redox")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cfgetospeed(termios_p: *const termios) -> speed_t {
//TODO
0
#[cfg(target_os = "linux")]
{
unsafe { (*termios_p).__c_ospeed }
}
#[cfg(target_os = "redox")]
{
//TODO
0
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cfsetispeed.html>.
#[cfg(target_os = "linux")]
///
/// Sets the input baud rate stored in the structure pointed to by `termios_p`
/// to `speed`.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cfsetispeed(termios_p: *mut termios, speed: speed_t) -> c_int {
match speed as usize {
// TODO redox impl
#[cfg(target_os = "linux")]
B0..=B38400 | B57600..=B4000000 => {
unsafe { (*termios_p).__c_ispeed = speed };
0
@@ -163,20 +218,18 @@ pub unsafe extern "C" fn cfsetispeed(termios_p: *mut termios, speed: speed_t) ->
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cfsetispeed.html>.
#[cfg(target_os = "redox")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cfsetispeed(termios_p: *mut termios, speed: speed_t) -> c_int {
//TODO
platform::ERRNO.set(errno::EINVAL);
-1
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cfsetospeed.html>.
#[cfg(target_os = "linux")]
///
/// Sets the output baud rate stored in the structure pointed to by `termios_p`
/// to `speed`.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cfsetospeed(termios_p: *mut termios, speed: speed_t) -> c_int {
match speed as usize {
// TODO redox impl
#[cfg(target_os = "linux")]
B0..=B38400 | B57600..=B4000000 => {
unsafe { (*termios_p).__c_ospeed = speed };
0
@@ -188,18 +241,18 @@ pub unsafe extern "C" fn cfsetospeed(termios_p: *mut termios, speed: speed_t) ->
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/cfsetospeed.html>.
#[cfg(target_os = "redox")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cfsetospeed(termios_p: *mut termios, speed: speed_t) -> c_int {
//TODO
platform::ERRNO.set(errno::EINVAL);
-1
}
/// Non-POSIX, 4.4 BSD extension
// TODO should be guarded by _BSD_SOURCE or _DEFAULT_SOURCE
/// Non-POSIX, 4.4 BSD extension, see
/// <https://www.man7.org/linux/man-pages/man3/cfsetispeed.3.html>.
///
/// See <https://www.man7.org/linux/man-pages/man3/cfsetispeed.3.html>.
/// Sets both the input and output baud rate stored in the structure pointed to
/// by `termios_p` to `speed`.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
///
/// # Implementation
/// Calls `cfsetispeed()` and if successful then calls `cfsetospeed()`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cfsetspeed(termios_p: *mut termios, speed: speed_t) -> c_int {
let r = unsafe { cfsetispeed(termios_p, speed) };
@@ -210,48 +263,109 @@ pub unsafe extern "C" fn cfsetspeed(termios_p: *mut termios, speed: speed_t) ->
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcflush.html>.
///
/// Discards data written to the object referred to by `fildes` but not
/// transmitted, or data received but not read, depending on the value of
/// `queue_selector`.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tcflush(fd: c_int, queue: c_int) -> c_int {
unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCFLSH, queue as *mut c_void) }
pub unsafe extern "C" fn tcflush(fildes: c_int, queue_selector: c_int) -> c_int {
match queue_selector {
TCIFLUSH | TCOFLUSH | TCIOFLUSH => unsafe {
sys_ioctl::ioctl(fildes, sys_ioctl::TCFLSH, queue_selector as *mut c_void)
},
_ => {
platform::ERRNO.set(errno::EINVAL);
-1
}
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcdrain.html>.
///
/// Blocks until all output written to the object referred to by `fildes` is
/// transmitted.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tcdrain(fd: c_int) -> c_int {
unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCSBRK, core::ptr::dangling_mut()) }
pub unsafe extern "C" fn tcdrain(fildes: c_int) -> c_int {
unsafe { sys_ioctl::ioctl(fildes, sys_ioctl::TCSBRK, core::ptr::dangling_mut()) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcsendbreak.html>.
///
/// Causes transmission of a continuous stream of zero-valued bits for at least
/// 0.25 seconds, and not more than 0.5 seconds.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
///
/// # Implementation
/// This function does not accept a non-zero duration. `_duration` is ignored
/// and assumed to be `0`. Matches behaviour of musl.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tcsendbreak(fd: c_int, _dur: c_int) -> c_int {
// non-zero duration is ignored by musl due to it being
// implementation-defined. we do the same.
unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCSBRK, core::ptr::null_mut()) }
pub unsafe extern "C" fn tcsendbreak(fildes: c_int, _duration: c_int) -> c_int {
unsafe { sys_ioctl::ioctl(fildes, sys_ioctl::TCSBRK, core::ptr::null_mut()) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcgetwinsize.html>.
///
/// Gets the terminal window size associated with the terminal referred to by
/// `fildes` and stores it in the winsize structure pointed to by `winsize_p`.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tcgetwinsize(fd: c_int, sws: *mut winsize) -> c_int {
unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCGWINSZ, sws.cast()) }
pub unsafe extern "C" fn tcgetwinsize(fildes: c_int, winsize_p: *mut winsize) -> c_int {
unsafe { sys_ioctl::ioctl(fildes, sys_ioctl::TIOCGWINSZ, winsize_p.cast()) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcsetwinsize.html>.
///
/// Sets the terminal window size associated with the terminal referred to by
/// `fildes` and stores it in the winsize structure pointed to by `winsize_p`.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
///
/// # Safety
/// It is undefined behaviour if the value of the winsize structure pointed to
/// by `winsize_p` was not derived from the result of a call to
/// `tcsgetwinsize()` on `fildes`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn tcsetwinsize(fd: c_int, sws: *const winsize) -> c_int {
unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCSWINSZ, sws.cast_mut().cast()) }
pub unsafe extern "C" fn tcsetwinsize(fildes: c_int, winsize_p: *const winsize) -> c_int {
unsafe { sys_ioctl::ioctl(fildes, sys_ioctl::TIOCSWINSZ, winsize_p.cast_mut().cast()) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcflow.html>.
///
/// Suspends or restarts transmission or reception of data on the object
/// referred to by `fildes`, depending on the value of `action`.
///
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
/// indicate the error.
#[unsafe(no_mangle)]
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
// implementation-defined. we do the same.
unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCXONC, action as *mut _) }
pub unsafe extern "C" fn tcflow(fildes: c_int, action: c_int) -> c_int {
match action {
TCIOFF | TCION | TCOOFF | TCOON => unsafe {
sys_ioctl::ioctl(fildes, sys_ioctl::TCXONC, action as *mut _)
},
_ => {
platform::ERRNO.set(errno::EINVAL);
-1
}
}
}
/// Non-POSIX, BSD extension
// TODO should be guarded by _BSD_SOURCE or _DEFAULT_SOURCE
/// Non-POSIX, BSD extension, see <https://www.man7.org/linux/man-pages/man3/cfmakeraw.3.html>.
///
/// See <https://www.man7.org/linux/man-pages/man3/cfmakeraw.3.html>.
/// Sets the terminal to something similar to "raw" mode: input is available
/// character by character, echoing is disabled, and all special processing of
/// terminal input and output characters is disabled.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cfmakeraw(termios_p: *mut termios) {
unsafe {
+8 -2
View File
@@ -715,10 +715,16 @@ pub unsafe extern "C" fn getwd(path_name: *mut c_char) -> *mut c_char {
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isatty.html>.
///
/// Checks whether `fildes`, an open file descriptor, is associated with a
/// terminal device.
///
/// Returns `1` to indicate `fildes` is associated with a terminal device or
/// returns `0` to indicate it is not.
#[unsafe(no_mangle)]
pub extern "C" fn isatty(fd: c_int) -> c_int {
pub extern "C" fn isatty(fildes: c_int) -> c_int {
let mut t = termios::termios::default();
if unsafe { termios::tcgetattr(fd, &raw mut t) == 0 } {
if unsafe { termios::tcgetattr(fildes, &raw mut t) == 0 } {
1
} else {
0