mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 19:17:43 +00:00
join: add support for -z option
This commit is contained in:
parent
4fc47aba61
commit
346415e1d2
3 changed files with 48 additions and 4 deletions
|
@ -25,6 +25,13 @@ enum FileNum {
|
|||
File2,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone)]
|
||||
enum LineEnding {
|
||||
Nul = 0,
|
||||
Newline = b'\n',
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Sep {
|
||||
Char(u8),
|
||||
|
@ -46,6 +53,7 @@ struct Settings {
|
|||
print_unpaired2: bool,
|
||||
print_joined: bool,
|
||||
ignore_case: bool,
|
||||
line_ending: LineEnding,
|
||||
separator: Sep,
|
||||
autoformat: bool,
|
||||
format: Vec<Spec>,
|
||||
|
@ -63,6 +71,7 @@ impl Default for Settings {
|
|||
print_unpaired2: false,
|
||||
print_joined: true,
|
||||
ignore_case: false,
|
||||
line_ending: LineEnding::Newline,
|
||||
separator: Sep::Whitespaces,
|
||||
autoformat: false,
|
||||
format: vec![],
|
||||
|
@ -75,14 +84,21 @@ impl Default for Settings {
|
|||
|
||||
/// Output representation.
|
||||
struct Repr<'a> {
|
||||
line_ending: LineEnding,
|
||||
separator: u8,
|
||||
format: &'a [Spec],
|
||||
empty: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
fn new(separator: u8, format: &'a [Spec], empty: &'a [u8]) -> Repr<'a> {
|
||||
fn new(
|
||||
line_ending: LineEnding,
|
||||
separator: u8,
|
||||
format: &'a [Spec],
|
||||
empty: &'a [u8],
|
||||
) -> Repr<'a> {
|
||||
Repr {
|
||||
line_ending,
|
||||
separator,
|
||||
format,
|
||||
empty,
|
||||
|
@ -133,6 +149,10 @@ impl<'a> Repr<'a> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_line_ending(&self) -> Result<(), std::io::Error> {
|
||||
stdout().write_all(&[self.line_ending as u8])
|
||||
}
|
||||
}
|
||||
|
||||
/// Input processing parameters.
|
||||
|
@ -260,6 +280,7 @@ impl<'a> State<'a> {
|
|||
name: &'a str,
|
||||
stdin: &'a Stdin,
|
||||
key: usize,
|
||||
line_ending: LineEnding,
|
||||
print_unpaired: bool,
|
||||
) -> State<'a> {
|
||||
let f = if name == "-" {
|
||||
|
@ -276,7 +297,7 @@ impl<'a> State<'a> {
|
|||
file_name: name,
|
||||
file_num,
|
||||
print_unpaired,
|
||||
lines: f.split(b'\n'),
|
||||
lines: f.split(line_ending as u8),
|
||||
seq: Vec::new(),
|
||||
line_num: 0,
|
||||
has_failed: false,
|
||||
|
@ -351,7 +372,7 @@ impl<'a> State<'a> {
|
|||
repr.print_fields(line2, other.key)?;
|
||||
}
|
||||
|
||||
stdout().write_all(&[b'\n'])?;
|
||||
repr.print_line_ending()?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -461,7 +482,7 @@ impl<'a> State<'a> {
|
|||
repr.print_fields(line, self.key)?;
|
||||
}
|
||||
|
||||
stdout().write_all(&[b'\n'])
|
||||
repr.print_line_ending()
|
||||
}
|
||||
|
||||
fn print_first_line(&self, repr: &Repr) -> Result<(), std::io::Error> {
|
||||
|
@ -540,6 +561,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
settings.headers = true;
|
||||
}
|
||||
|
||||
if matches.is_present("z") {
|
||||
settings.line_ending = LineEnding::Nul;
|
||||
}
|
||||
|
||||
let file1 = matches.value_of("file1").unwrap();
|
||||
let file2 = matches.value_of("file2").unwrap();
|
||||
|
||||
|
@ -646,6 +671,12 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2",
|
|||
"treat the first line in each file as field headers, \
|
||||
print them without trying to pair them",
|
||||
))
|
||||
.arg(
|
||||
Arg::with_name("z")
|
||||
.short("z")
|
||||
.long("zero-terminated")
|
||||
.help("line delimiter is NUL, not newline"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("file1")
|
||||
.required(true)
|
||||
|
@ -668,6 +699,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> Result<(), std::io::Err
|
|||
file1,
|
||||
&stdin,
|
||||
settings.key1,
|
||||
settings.line_ending,
|
||||
settings.print_unpaired1,
|
||||
);
|
||||
|
||||
|
@ -676,6 +708,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> Result<(), std::io::Err
|
|||
file2,
|
||||
&stdin,
|
||||
settings.key2,
|
||||
settings.line_ending,
|
||||
settings.print_unpaired2,
|
||||
);
|
||||
|
||||
|
@ -705,6 +738,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> Result<(), std::io::Err
|
|||
};
|
||||
|
||||
let repr = Repr::new(
|
||||
settings.line_ending,
|
||||
match settings.separator {
|
||||
Sep::Char(sep) => sep,
|
||||
_ => b' ',
|
||||
|
|
|
@ -346,3 +346,13 @@ fn non_unicode() {
|
|||
.succeeds()
|
||||
.stdout_only_fixture("non-unicode.expected");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn null_line_endings() {
|
||||
new_ucmd!()
|
||||
.arg("-z")
|
||||
.arg("non-unicode_1.bin")
|
||||
.arg("non-unicode_2.bin")
|
||||
.succeeds()
|
||||
.stdout_only_fixture("z.expected");
|
||||
}
|
||||
|
|
BIN
tests/fixtures/join/z.expected
vendored
Normal file
BIN
tests/fixtures/join/z.expected
vendored
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue