1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

join: make autoformat actually construct a format

Makes the -o auto option construct a format at initialization, rather
than try to handle it as a special case when printing lines. Fixes bugs
when combined with -e, especially when combined with -a.
This commit is contained in:
Justin Tracey 2021-08-31 13:25:03 -04:00 committed by Michael Debertol
parent 2bd556e252
commit 575fbd4cb7
2 changed files with 47 additions and 24 deletions

View file

@ -11,7 +11,7 @@
extern crate uucore; extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::cmp::{min, Ordering}; use std::cmp::Ordering;
use std::fs::File; use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Lines, Stdin}; use std::io::{stdin, BufRead, BufReader, Lines, Stdin};
@ -102,17 +102,12 @@ impl<'a> Repr<'a> {
} }
/// Print each field except the one at the index. /// Print each field except the one at the index.
fn print_fields(&self, line: &Line, index: usize, max_fields: Option<usize>) { fn print_fields(&self, line: &Line, index: usize) {
for i in 0..min(max_fields.unwrap_or(usize::max_value()), line.fields.len()) { for i in 0..line.fields.len() {
if i != index { if i != index {
print!("{}{}", self.separator, line.fields[i]); print!("{}{}", self.separator, line.fields[i]);
} }
} }
if let Some(n) = max_fields {
for _ in line.fields.len()..n {
print!("{}", self.separator)
}
}
} }
/// Print each field or the empty filler if the field is not set. /// Print each field or the empty filler if the field is not set.
@ -233,7 +228,6 @@ struct State<'a> {
print_unpaired: bool, print_unpaired: bool,
lines: Lines<Box<dyn BufRead + 'a>>, lines: Lines<Box<dyn BufRead + 'a>>,
seq: Vec<Line>, seq: Vec<Line>,
max_fields: Option<usize>,
line_num: usize, line_num: usize,
has_failed: bool, has_failed: bool,
} }
@ -262,7 +256,6 @@ impl<'a> State<'a> {
print_unpaired, print_unpaired,
lines: f.lines(), lines: f.lines(),
seq: Vec::new(), seq: Vec::new(),
max_fields: None,
line_num: 0, line_num: 0,
has_failed: false, has_failed: false,
} }
@ -329,8 +322,8 @@ impl<'a> State<'a> {
}); });
} else { } else {
repr.print_field(key); repr.print_field(key);
repr.print_fields(line1, self.key, self.max_fields); repr.print_fields(line1, self.key);
repr.print_fields(line2, other.key, other.max_fields); repr.print_fields(line2, other.key);
} }
println!(); println!();
@ -361,14 +354,15 @@ impl<'a> State<'a> {
!self.seq.is_empty() !self.seq.is_empty()
} }
fn initialize(&mut self, read_sep: Sep, autoformat: bool) { fn initialize(&mut self, read_sep: Sep, autoformat: bool) -> usize {
if let Some(line) = self.read_line(read_sep) { if let Some(line) = self.read_line(read_sep) {
if autoformat {
self.max_fields = Some(line.fields.len());
}
self.seq.push(line); self.seq.push(line);
if autoformat {
return self.seq[0].fields.len();
}
} }
0
} }
fn finalize(&mut self, input: &Input, repr: &Repr) { fn finalize(&mut self, input: &Input, repr: &Repr) {
@ -431,7 +425,7 @@ impl<'a> State<'a> {
}); });
} else { } else {
repr.print_field(line.get_field(self.key)); repr.print_field(line.get_field(self.key));
repr.print_fields(line, self.key, self.max_fields); repr.print_fields(line, self.key);
} }
println!(); println!();
@ -512,7 +506,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
crash!(1, "both files cannot be standard input"); crash!(1, "both files cannot be standard input");
} }
exec(file1, file2, &settings) exec(file1, file2, settings)
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
@ -622,7 +616,7 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2",
) )
} }
fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 { fn exec(file1: &str, file2: &str, settings: Settings) -> i32 {
let stdin = stdin(); let stdin = stdin();
let mut state1 = State::new( let mut state1 = State::new(
@ -647,18 +641,34 @@ fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 {
settings.check_order, settings.check_order,
); );
let format = if settings.autoformat {
let mut format = vec![Spec::Key];
let mut initialize = |state: &mut State| {
let max_fields = state.initialize(settings.separator, settings.autoformat);
for i in 0..max_fields {
if i != state.key {
format.push(Spec::Field(state.file_num, i));
}
}
};
initialize(&mut state1);
initialize(&mut state2);
format
} else {
state1.initialize(settings.separator, settings.autoformat);
state2.initialize(settings.separator, settings.autoformat);
settings.format
};
let repr = Repr::new( let repr = Repr::new(
match settings.separator { match settings.separator {
Sep::Char(sep) => sep, Sep::Char(sep) => sep,
_ => ' ', _ => ' ',
}, },
&settings.format, &format,
&settings.empty, &settings.empty,
); );
state1.initialize(settings.separator, settings.autoformat);
state2.initialize(settings.separator, settings.autoformat);
if settings.headers { if settings.headers {
state1.print_headers(&state2, &repr); state1.print_headers(&state2, &repr);
state1.reset_read_line(&input); state1.reset_read_line(&input);

View file

@ -227,6 +227,19 @@ fn autoformat() {
.pipe_in("1 x y z\n2 p") .pipe_in("1 x y z\n2 p")
.succeeds() .succeeds()
.stdout_only("1 x y z a\n2 p b\n"); .stdout_only("1 x y z a\n2 p b\n");
new_ucmd!()
.arg("-")
.arg("fields_2.txt")
.arg("-a")
.arg("1")
.arg("-o")
.arg("auto")
.arg("-e")
.arg(".")
.pipe_in("1 x y z\n2 p\n99 a b\n")
.succeeds()
.stdout_only("1 x y z a\n2 p . . b\n99 a b . .\n");
} }
#[test] #[test]