mirror of
https://github.com/uutils/coreutils
synced 2024-09-06 16:03: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:
parent
2bd556e252
commit
575fbd4cb7
|
@ -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);
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in a new issue