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;
use clap::{crate_version, App, Arg};
use std::cmp::{min, Ordering};
use std::cmp::Ordering;
use std::fs::File;
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.
fn print_fields(&self, line: &Line, index: usize, max_fields: Option<usize>) {
for i in 0..min(max_fields.unwrap_or(usize::max_value()), line.fields.len()) {
fn print_fields(&self, line: &Line, index: usize) {
for i in 0..line.fields.len() {
if i != index {
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.
@ -233,7 +228,6 @@ struct State<'a> {
print_unpaired: bool,
lines: Lines<Box<dyn BufRead + 'a>>,
seq: Vec<Line>,
max_fields: Option<usize>,
line_num: usize,
has_failed: bool,
}
@ -262,7 +256,6 @@ impl<'a> State<'a> {
print_unpaired,
lines: f.lines(),
seq: Vec::new(),
max_fields: None,
line_num: 0,
has_failed: false,
}
@ -329,8 +322,8 @@ impl<'a> State<'a> {
});
} else {
repr.print_field(key);
repr.print_fields(line1, self.key, self.max_fields);
repr.print_fields(line2, other.key, other.max_fields);
repr.print_fields(line1, self.key);
repr.print_fields(line2, other.key);
}
println!();
@ -361,14 +354,15 @@ impl<'a> State<'a> {
!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 autoformat {
self.max_fields = Some(line.fields.len());
}
self.seq.push(line);
if autoformat {
return self.seq[0].fields.len();
}
}
0
}
fn finalize(&mut self, input: &Input, repr: &Repr) {
@ -431,7 +425,7 @@ impl<'a> State<'a> {
});
} else {
repr.print_field(line.get_field(self.key));
repr.print_fields(line, self.key, self.max_fields);
repr.print_fields(line, self.key);
}
println!();
@ -512,7 +506,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
crash!(1, "both files cannot be standard input");
}
exec(file1, file2, &settings)
exec(file1, file2, settings)
}
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 mut state1 = State::new(
@ -647,18 +641,34 @@ fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 {
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(
match settings.separator {
Sep::Char(sep) => sep,
_ => ' ',
},
&settings.format,
&format,
&settings.empty,
);
state1.initialize(settings.separator, settings.autoformat);
state2.initialize(settings.separator, settings.autoformat);
if settings.headers {
state1.print_headers(&state2, &repr);
state1.reset_read_line(&input);

View file

@ -227,6 +227,19 @@ fn autoformat() {
.pipe_in("1 x y z\n2 p")
.succeeds()
.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]