Skip to content

Commit cf31a00

Browse files
dragutreisxtqqczzeoech3
committed
tsort: handle stdout write errors without panicking
Co-authored-by: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Co-authored-by: oech3 <79379754+oech3@users.noreply.github.com>
1 parent 5aade31 commit cf31a00

3 files changed

Lines changed: 28 additions & 9 deletions

File tree

src/uu/tsort/locales/en-US.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ tsort-error-loop = input contains a loop:
99
tsort-error-extra-operand = extra operand { $operand }
1010
Try '{ $util } --help' for more information.
1111
tsort-error-at-least-one-input = at least one input
12+
tsort-error-read = read error: { $error }
13+
tsort-error-write = write error: { $error }

src/uu/tsort/locales/fr-FR.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ tsort-error-loop = l'entrée contient une boucle :
99
tsort-error-extra-operand = opérande supplémentaire { $operand }
1010
Essayez '{ $util } --help' pour plus d'informations.
1111
tsort-error-at-least-one-input = au moins une entrée
12+
tsort-error-read = erreur de lecture : { $error }
13+
tsort-error-write = erreur d'écriture : { $error }

src/uu/tsort/src/tsort.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ use std::collections::VecDeque;
1010
use std::collections::hash_map::Entry;
1111
use std::ffi::OsString;
1212
use std::fs::File;
13-
use std::io::{self, BufRead, BufReader};
13+
use std::io::{self, BufRead, BufReader, Write};
1414
use string_interner::StringInterner;
1515
use string_interner::backend::BucketBackend;
1616
use thiserror::Error;
1717
use uucore::display::Quotable;
18-
use uucore::error::{UError, UResult, USimpleError};
18+
use uucore::error::{UError, UResult, USimpleError, strip_errno};
1919
use uucore::{format_usage, show, translate};
2020

2121
// short types for switching interning behavior on the fly.
@@ -102,7 +102,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
102102
process_input(reader, &mut g)?;
103103
}
104104

105-
g.run_tsort();
105+
g.run_tsort()?;
106106
Ok(())
107107
}
108108

@@ -148,9 +148,13 @@ enum TsortError {
148148
#[error("{input}: {message}", input = .0, message = translate!("tsort-error-loop"))]
149149
Loop(String),
150150

151-
/// Wrapper for bubbling up IO errors
152-
#[error("{0}")]
153-
IO(#[from] io::Error),
151+
/// Read error.
152+
#[error("{}", translate!("tsort-error-read", "error" => strip_errno(.0)))]
153+
Read(io::Error),
154+
155+
/// Write error.
156+
#[error("{}", translate!("tsort-error-write", "error" => strip_errno(.0)))]
157+
Write(io::Error),
154158
}
155159

156160
// Auxiliary struct, just for printing loop nodes via show! macro
@@ -173,7 +177,7 @@ fn process_input<R: BufRead>(reader: R, graph: &mut Graph) -> Result<(), TsortEr
173177
if e.kind() == io::ErrorKind::IsADirectory {
174178
TsortError::IsDir(graph.name())
175179
} else {
176-
e.into()
180+
TsortError::Read(e)
177181
}
178182
})?;
179183
for token in line.split_whitespace() {
@@ -276,7 +280,7 @@ impl Graph {
276280
}
277281

278282
/// Implementation of algorithm T from TAOCP (Don. Knuth), vol. 1.
279-
fn run_tsort(&mut self) {
283+
fn run_tsort(&mut self) -> Result<(), TsortError> {
280284
let mut independent_nodes_queue: VecDeque<Sym> = self
281285
.nodes
282286
.iter()
@@ -289,14 +293,23 @@ impl Graph {
289293
})
290294
.collect();
291295

296+
let stdout = io::stdout();
297+
let mut handle = stdout.lock();
298+
let mut res: io::Result<()> = Ok(());
299+
292300
// Sort by resolved string for deterministic output
293301
independent_nodes_queue
294302
.make_contiguous()
295303
.sort_unstable_by(|a, b| self.get_node_name(*a).cmp(self.get_node_name(*b)));
296304

297305
while !self.nodes.is_empty() {
298306
let v = self.find_next_node(&mut independent_nodes_queue);
299-
println!("{}", self.get_node_name(v));
307+
308+
// Attempt write but continue regardless on error
309+
if res.is_ok() {
310+
res = writeln!(handle, "{}", self.get_node_name(v));
311+
}
312+
300313
if let Some(node_to_process) = self.nodes.remove(&v) {
301314
for successor_name in node_to_process.successor_tokens.into_iter().rev() {
302315
// we reverse to match GNU tsort order
@@ -311,6 +324,8 @@ impl Graph {
311324
}
312325
}
313326
}
327+
328+
res.map_err(TsortError::Write)
314329
}
315330
pub fn indegree(&self, sym: Sym) -> Option<usize> {
316331
self.nodes.get(&sym).map(|data| data.predecessor_count)

0 commit comments

Comments
 (0)