use crate::libc;
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() {
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() {
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");
}
#[test]
fn test_strlcat_empty_src() {
let mut dst = [0u8; 10];
dst[..2].copy_from_slice(b"hi");
let src = crate::c!("");
let ret = unsafe { strlcat(dst.as_mut_ptr(), src, dst.len()) };
assert_eq!(ret, 2);
assert_eq!(&dst[..3], b"hi\0");
}
#[test]
fn test_strlcat_exact_fit() {
let mut dst = [0u8; 10];
dst[..2].copy_from_slice(b"ab");
let src = crate::c!("cd");
let ret = unsafe { strlcat(dst.as_mut_ptr(), src, 5) };
assert_eq!(ret, 4);
assert_eq!(&dst[..5], b"abcd\0");
}
#[test]
fn test_strlcat_truncation_returns_copied_len() {
let mut dst = [0u8; 16];
dst[..3].copy_from_slice(b"foo");
let src = crate::c!("barbaz");
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_no_room_left() {
let mut dst = [b'x'; 16];
let src = crate::c!("y");
let ret = unsafe { strlcat(dst.as_mut_ptr(), src, 3) };
assert_eq!(ret, 3);
assert_eq!(dst[3], b'\0');
}
#[test]
fn test_strlcat_size_zero() {
let mut dst = [b'z'; 8];
let src = crate::c!("abc");
let ret = unsafe { strlcat(dst.as_mut_ptr(), src, 0) };
assert_eq!(ret, 0);
assert_eq!(dst[0], b'\0');
}
#[test]
fn test_strlcat_repeated_append() {
let mut dst = [0u8; 16];
let ret1 = unsafe { strlcat(dst.as_mut_ptr(), crate::c!("ab"), dst.len()) };
assert_eq!(ret1, 2);
let ret2 = unsafe { strlcat(dst.as_mut_ptr(), crate::c!("cde"), dst.len()) };
assert_eq!(ret2, 5);
assert_eq!(&dst[..6], b"abcde\0");
}
}