Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions src/uu/sort/src/ext_sort/threaded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@

use std::cmp::Ordering;
use std::fs::File;
use std::io::{Read, Write, stderr};
use std::io::{Read, Write};
use std::path::PathBuf;
use std::sync::mpsc::{Receiver, SyncSender};
use std::thread;

use itertools::Itertools;
use uucore::error::{UResult, strip_errno};
use uucore::show_error;
use uucore::translate;

use crate::Output;
use crate::chunks::RecycledChunk;
Expand Down Expand Up @@ -52,6 +54,8 @@ pub fn ext_sort(

// Test if compression program exists and works, disable if not
let mut effective_settings = settings.clone();
// Keep the error until we know compression is actually needed.
let mut compress_prog_error = None;
if let Some(ref prog) = settings.compress_prog {
// Test the compression program by trying to spawn it
match std::process::Command::new(prog)
Expand All @@ -65,12 +69,7 @@ pub fn ext_sort(
let _ = child.kill();
}
Err(err) => {
// Print the error and disable compression
let _ = writeln!(
stderr(),
"sort: could not run compress program '{prog}': {}",
strip_errno(&err)
);
compress_prog_error = Some((prog.clone(), err));
effective_settings.compress_prog = None;
}
}
Expand All @@ -80,6 +79,7 @@ pub fn ext_sort(
reader_writer::<_, WriteableCompressedTmpFile>(
files,
&effective_settings,
None,
&sorted_receiver,
recycled_sender,
output,
Expand All @@ -89,6 +89,7 @@ pub fn ext_sort(
reader_writer::<_, WriteablePlainTmpFile>(
files,
&effective_settings,
compress_prog_error,
&sorted_receiver,
recycled_sender,
output,
Expand All @@ -103,6 +104,7 @@ fn reader_writer<
>(
files: F,
settings: &GlobalSettings,
compress_prog_error: Option<(String, std::io::Error)>,
receiver: &Receiver<Chunk>,
sender: SyncSender<Chunk>,
output: Output,
Expand All @@ -125,6 +127,7 @@ fn reader_writer<
separator,
buffer_size,
settings,
compress_prog_error,
receiver,
sender,
)?;
Expand Down Expand Up @@ -207,12 +210,14 @@ enum ReadResult<I: WriteableTmpFile> {
WroteChunksToFile { tmp_files: Vec<I::Closed> },
}
/// The function that is executed on the reader/writer thread.
#[allow(clippy::too_many_arguments)]
fn read_write_loop<I: WriteableTmpFile>(
mut files: impl Iterator<Item = UResult<Box<dyn Read + Send>>>,
tmp_dir: &mut TmpDirWrapper,
separator: u8,
buffer_size: usize,
settings: &GlobalSettings,
compress_prog_error: Option<(String, std::io::Error)>,
receiver: &Receiver<Chunk>,
sender: SyncSender<Chunk>,
) -> UResult<ReadResult<I>> {
Expand Down Expand Up @@ -249,6 +254,18 @@ fn read_write_loop<I: WriteableTmpFile>(
}
}

// The input did not fit into the first two in-memory chunks.
if let Some((prog, err)) = compress_prog_error {
show_error!(
"{}",
translate!(
"sort-compress-prog-execution-failed",
"prog" => prog,
"error" => strip_errno(&err)
)
);
}

let mut sender_option = Some(sender);
let mut tmp_files = vec![];
loop {
Expand Down
17 changes: 16 additions & 1 deletion tests/by-util/test_sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1495,8 +1495,9 @@ fn test_compress_fail() {
"-n",
"--compress-program",
"nonexistent-program",
// Force external sorting by using 1K buffer size so ext_sort.txt spills to disk
"-S",
"10",
"1K",
])
.succeeds();

Expand All @@ -1516,6 +1517,20 @@ fn test_compress_fail() {
assert_eq!(result.stdout_str(), expected);
}

#[test]
#[cfg(unix)]
fn test_input_error_before_compression_is_needed() {
let (at, mut ucmd) = at_and_ucmd!();
at.write("input", "b\na\n");
at.mkdir("directory");

ucmd.args(&["--compress-program=nonexistent", "input", "directory"])
.fails_with_code(2)
.no_stdout()
.stderr_contains("Is a directory")
.stderr_does_not_contain("compress program");
}

#[test]
fn test_merge_batches() {
new_ucmd!()
Expand Down
Loading