diff --git a/src/uu/ptx/src/ptx.rs b/src/uu/ptx/src/ptx.rs index 28d19cdbdfd..cfb268ef275 100644 --- a/src/uu/ptx/src/ptx.rs +++ b/src/uu/ptx/src/ptx.rs @@ -7,7 +7,7 @@ use std::cmp; use std::cmp::PartialEq; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeSet, HashSet}; use std::ffi::{OsStr, OsString}; use std::fmt::Write as FmtWrite; use std::fs::File; @@ -269,10 +269,10 @@ struct FileContent { offset: usize, } -type FileMap = HashMap; +type FileMap = Vec<(OsString, FileContent)>; fn read_input(input_files: &[OsString]) -> std::io::Result { - let mut file_map: FileMap = HashMap::new(); + let mut file_map: FileMap = FileMap::new(); let mut offset: usize = 0; for filename in input_files { let reader: BufReader> = BufReader::new(if filename == "-" { @@ -287,14 +287,14 @@ fn read_input(input_files: &[OsString]) -> std::io::Result { // Since we will be jumping around the line a lot, we dump the content into a Vec, which can be indexed in constant time. let chars_lines: Vec> = lines.iter().map(|x| x.chars().collect()).collect(); let size = lines.len(); - file_map.insert( + file_map.push(( filename.clone(), FileContent { lines, chars_lines, offset, }, - ); + )); offset += size; } Ok(file_map) @@ -751,8 +751,17 @@ fn write_traditional_output( } for word_ref in words { - let file_map_value: &FileContent = file_map - .get(&word_ref.filename) + // Since `ptx` accepts duplicate file arguments (e.g., `ptx file file`), + // simply looking up by filename is ambiguous. + // We use the `global_line_nr` (which is unique across the entire input stream) + // to identify which file covers this line. + let (_, file_map_value) = file_map + .iter() + .find(|(name, content)| { + name == &word_ref.filename + && word_ref.global_line_nr >= content.offset + && word_ref.global_line_nr < content.offset + content.lines.len() + }) .expect("Missing file in file map"); let FileContent { ref lines, diff --git a/tests/by-util/test_ptx.rs b/tests/by-util/test_ptx.rs index c9ecb5c22e2..bddac5052da 100644 --- a/tests/by-util/test_ptx.rs +++ b/tests/by-util/test_ptx.rs @@ -301,3 +301,11 @@ fn test_unicode_truncation_alignment() { .succeeds() .stdout_only(" / bar\n föö/\n"); } + +#[test] +fn test_duplicate_input_files() { + new_ucmd!() + .args(&["one_word", "one_word"]) + .succeeds() + .stdout_is(" rust\n rust\n"); +} diff --git a/tests/fixtures/ptx/one_word b/tests/fixtures/ptx/one_word new file mode 100644 index 00000000000..871732e64f9 --- /dev/null +++ b/tests/fixtures/ptx/one_word @@ -0,0 +1 @@ +rust