mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37: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
2 changed files with 47 additions and 24 deletions
|
@ -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,15 +354,16 @@ 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) {
|
||||||
if self.has_line() && self.print_unpaired {
|
if self.has_line() && self.print_unpaired {
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue