ztmux 0.1.0

A Rust port of tmux — the full terminal multiplexer, server and client
Documentation
use crate::libc;

/// The `strlcat()` function appends the NUL-terminated string src to the end of dst.
/// It will append at most size - strlen(dst) - 1 bytes, NUL-terminating the result.
/// C `vendor/tmux/compat/strlcat.c:32`: `size_t strlcat(char *dst, const char *src, size_t siz)`
pub unsafe fn strlcat(dst: *mut u8, src: *const u8, size: usize) -> usize {
    unsafe {
        let dst_strlen = libc::strnlen(dst, size);
        let src_strlen = libc::strnlen(src, size.saturating_sub(dst_strlen).saturating_sub(1));

        core::ptr::copy_nonoverlapping(src, dst.add(dst_strlen), src_strlen);
        *dst.add(dst_strlen + src_strlen) = b'\0';

        dst_strlen + src_strlen
    }
}

#[expect(clippy::disallowed_methods)]
pub unsafe fn strlcat_(dst: *mut u8, src: &str, size: usize) -> usize {
    unsafe {
        let dst_strlen = libc::strnlen(dst, size);
        let src_strlen = src
            .len()
            .min(size.saturating_sub(dst_strlen).saturating_sub(1));

        core::ptr::copy_nonoverlapping(src.as_ptr(), dst.add(dst_strlen), src_strlen);
        *dst.add(dst_strlen + src_strlen) = b'\0';

        dst_strlen + src_strlen
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_strlcat_append() {
        // "foo" + "bar" fits: return value is combined length, NUL-terminated.
        let mut dst = [0u8; 10];
        dst[..3].copy_from_slice(b"foo");
        let src = crate::c!("bar");
        let ret = unsafe { strlcat(dst.as_mut_ptr(), src, dst.len()) };
        assert_eq!(ret, 6);
        assert_eq!(&dst[..7], b"foobar\0");
    }

    #[test]
    fn test_strlcat_truncation() {
        // size=5, dst holds "foo": only 5-3-1=1 byte of src is appended.
        let mut dst = [0u8; 10];
        dst[..3].copy_from_slice(b"foo");
        let src = crate::c!("bar");
        let ret = unsafe { strlcat(dst.as_mut_ptr(), src, 5) };
        assert_eq!(ret, 4);
        assert_eq!(&dst[..5], b"foob\0");
    }

    #[test]
    fn test_strlcat_empty_dst() {
        let mut dst = [0u8; 10];
        let src = crate::c!("hi");
        let ret = unsafe { strlcat(dst.as_mut_ptr(), src, dst.len()) };
        assert_eq!(ret, 2);
        assert_eq!(&dst[..3], b"hi\0");
    }
}