From 69d35d75d9692e76ea017f5012ad7e38b84c3992 Mon Sep 17 00:00:00 2001 From: A Farzat Date: Fri, 19 Jun 2026 16:33:13 +0300 Subject: Fix append_paths when input does not end in NUL --- src/path_list_editor.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/path_list_editor.rs b/src/path_list_editor.rs index e25b0db..9addb7f 100644 --- a/src/path_list_editor.rs +++ b/src/path_list_editor.rs @@ -5,19 +5,44 @@ use std::{ os::unix::ffi::OsStrExt, }; +const NUL: &[u8] = b"\0"; + pub fn append_paths( - mut input: R, + input: R, mut output: W, new_paths: &[OsString], ) -> std::io::Result<()> { - std::io::copy(&mut input, &mut output)?; + let last_byte_in_input = copy_while_tracking_last_byte(input, &mut output)?; + // Add NUL if there is input and it does not end in NUL. + if matches!(last_byte_in_input, Some(byte) if byte != 0) { + output.write_all(NUL)?; + } for path in new_paths { output.write_all(path.as_bytes())?; - output.write_all(b"\0")?; + output.write_all(NUL)?; } Ok(()) } +fn copy_while_tracking_last_byte( + mut input: R, + mut output: W, +) -> std::io::Result> { + let mut buf = [0; 8192]; + let mut last = None; + + loop { + let read_bytes = input.read(&mut buf)?; + if read_bytes == 0 { + break; + } + + last = Some(buf[read_bytes - 1]); + output.write_all(&buf[..read_bytes])?; + } + Ok(last) +} + pub fn prepend_paths( mut input: R, mut output: W, @@ -25,7 +50,7 @@ pub fn prepend_paths( ) -> std::io::Result<()> { for path in new_paths { output.write_all(path.as_bytes())?; - output.write_all(b"\0")?; + output.write_all(NUL)?; } std::io::copy(&mut input, &mut output)?; Ok(()) @@ -46,7 +71,7 @@ pub fn remove_paths( let os_string_segment = OsStr::from_bytes(segment).to_os_string(); if !unwanted_paths.contains(&os_string_segment) { output.write_all(segment)?; - output.write_all(b"\0")?; + output.write_all(NUL)?; } } Ok(()) @@ -108,6 +133,36 @@ mod tests { assert_eq!(output, b"a.rs\0b.rs\0a.rs\0"); } + #[test] + fn append_paths_inserts_separator_when_stdin_is_not_null_terminated() { + let input = b"a.rs"; + let mut output = Vec::new(); + + append_paths(&input[..], &mut output, &[OsString::from("b.rs")]).unwrap(); + + assert_eq!(output, b"a.rs\0b.rs\0"); + } + + #[test] + fn append_paths_does_not_insert_extra_separator_when_stdin_is_null_terminated() { + let input = b"a.rs\0"; + let mut output = Vec::new(); + + append_paths(&input[..], &mut output, &[OsString::from("b.rs")]).unwrap(); + + assert_eq!(output, b"a.rs\0b.rs\0"); + } + + #[test] + fn append_paths_handles_partially_terminated_input_sequence() { + let input = b"a.rs\0b.rs"; + let mut output = Vec::new(); + + append_paths(&input[..], &mut output, &[OsString::from("c.rs")]).unwrap(); + + assert_eq!(output, b"a.rs\0b.rs\0c.rs\0"); + } + #[test] fn prepend_paths_works_with_empty_stdin_and_args() { let input = b""; -- cgit v1.3.1