1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 19:17:43 +00:00

Merge pull request #2880 from jtracey/join-z

join: add support for `-z` option
This commit is contained in:
Sylvestre Ledru 2022-01-17 08:42:42 +01:00 committed by GitHub
commit f0095329a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 4 deletions

View file

@ -25,6 +25,13 @@ enum FileNum {
File2, File2,
} }
#[repr(u8)]
#[derive(Copy, Clone)]
enum LineEnding {
Nul = 0,
Newline = b'\n',
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
enum Sep { enum Sep {
Char(u8), Char(u8),
@ -46,6 +53,7 @@ struct Settings {
print_unpaired2: bool, print_unpaired2: bool,
print_joined: bool, print_joined: bool,
ignore_case: bool, ignore_case: bool,
line_ending: LineEnding,
separator: Sep, separator: Sep,
autoformat: bool, autoformat: bool,
format: Vec<Spec>, format: Vec<Spec>,
@ -63,6 +71,7 @@ impl Default for Settings {
print_unpaired2: false, print_unpaired2: false,
print_joined: true, print_joined: true,
ignore_case: false, ignore_case: false,
line_ending: LineEnding::Newline,
separator: Sep::Whitespaces, separator: Sep::Whitespaces,
autoformat: false, autoformat: false,
format: vec![], format: vec![],
@ -75,14 +84,21 @@ impl Default for Settings {
/// Output representation. /// Output representation.
struct Repr<'a> { struct Repr<'a> {
line_ending: LineEnding,
separator: u8, separator: u8,
format: &'a [Spec], format: &'a [Spec],
empty: &'a [u8], empty: &'a [u8],
} }
impl<'a> Repr<'a> { 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 { Repr {
line_ending,
separator, separator,
format, format,
empty, empty,
@ -133,6 +149,10 @@ impl<'a> Repr<'a> {
} }
Ok(()) Ok(())
} }
fn print_line_ending(&self) -> Result<(), std::io::Error> {
stdout().write_all(&[self.line_ending as u8])
}
} }
/// Input processing parameters. /// Input processing parameters.
@ -260,6 +280,7 @@ impl<'a> State<'a> {
name: &'a str, name: &'a str,
stdin: &'a Stdin, stdin: &'a Stdin,
key: usize, key: usize,
line_ending: LineEnding,
print_unpaired: bool, print_unpaired: bool,
) -> State<'a> { ) -> State<'a> {
let f = if name == "-" { let f = if name == "-" {
@ -276,7 +297,7 @@ impl<'a> State<'a> {
file_name: name, file_name: name,
file_num, file_num,
print_unpaired, print_unpaired,
lines: f.split(b'\n'), lines: f.split(line_ending as u8),
seq: Vec::new(), seq: Vec::new(),
line_num: 0, line_num: 0,
has_failed: false, has_failed: false,
@ -351,7 +372,7 @@ impl<'a> State<'a> {
repr.print_fields(line2, other.key)?; 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)?; 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> { 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; settings.headers = true;
} }
if matches.is_present("z") {
settings.line_ending = LineEnding::Nul;
}
let file1 = matches.value_of("file1").unwrap(); let file1 = matches.value_of("file1").unwrap();
let file2 = matches.value_of("file2").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, \ "treat the first line in each file as field headers, \
print them without trying to pair them", 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(
Arg::with_name("file1") Arg::with_name("file1")
.required(true) .required(true)
@ -668,6 +699,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> Result<(), std::io::Err
file1, file1,
&stdin, &stdin,
settings.key1, settings.key1,
settings.line_ending,
settings.print_unpaired1, settings.print_unpaired1,
); );
@ -676,6 +708,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> Result<(), std::io::Err
file2, file2,
&stdin, &stdin,
settings.key2, settings.key2,
settings.line_ending,
settings.print_unpaired2, settings.print_unpaired2,
); );
@ -705,6 +738,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> Result<(), std::io::Err
}; };
let repr = Repr::new( let repr = Repr::new(
settings.line_ending,
match settings.separator { match settings.separator {
Sep::Char(sep) => sep, Sep::Char(sep) => sep,
_ => b' ', _ => b' ',

View file

@ -346,3 +346,13 @@ fn non_unicode() {
.succeeds() .succeeds()
.stdout_only_fixture("non-unicode.expected"); .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

Binary file not shown.