diff --git a/src/uu/comm/src/comm.rs b/src/uu/comm/src/comm.rs index 26e70403793..088a921eb5e 100644 --- a/src/uu/comm/src/comm.rs +++ b/src/uu/comm/src/comm.rs @@ -13,6 +13,7 @@ use std::fs::File; use std::io::{self, stdin, BufRead, BufReader, Stdin}; use std::path::Path; use uucore::error::{FromIo, UResult}; +use uucore::line_ending::LineEnding; use uucore::{format_usage, help_about, help_usage}; use clap::{crate_version, Arg, ArgAction, ArgMatches, Command}; @@ -40,38 +41,6 @@ fn column_width(col: &str, opts: &ArgMatches) -> usize { } } -#[repr(u8)] -#[derive(Clone, Copy)] -enum LineEnding { - Newline = b'\n', - Nul = 0, -} - -impl From for u8 { - fn from(line_ending: LineEnding) -> Self { - line_ending as Self - } -} - -impl From for LineEnding { - fn from(is_zero_terminated: bool) -> Self { - if is_zero_terminated { - Self::Nul - } else { - Self::Newline - } - } -} - -impl Display for LineEnding { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Newline => writeln!(f), - Self::Nul => write!(f, "\0"), - } - } -} - enum Input { Stdin(Stdin), FileIn(BufReader), diff --git a/src/uu/cut/src/cut.rs b/src/uu/cut/src/cut.rs index 68ad566d7c4..0251a3c7752 100644 --- a/src/uu/cut/src/cut.rs +++ b/src/uu/cut/src/cut.rs @@ -10,6 +10,7 @@ use bstr::io::BufReadExt; use clap::{crate_version, Arg, ArgAction, Command}; use is_terminal::IsTerminal; +use uucore::line_ending::LineEnding; use std::fs::File; use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write}; use std::path::Path; @@ -30,7 +31,7 @@ const AFTER_HELP: &str = help_section!("after help", "cut.md"); struct Options { out_delim: Option, - zero_terminated: bool, + line_ending: LineEnding, } enum Delimiter { @@ -42,7 +43,7 @@ struct FieldOptions { delimiter: Delimiter, out_delimiter: Option, only_delimited: bool, - zero_terminated: bool, + line_ending: LineEnding, } enum Mode { @@ -68,7 +69,7 @@ fn list_to_ranges(list: &str, complement: bool) -> Result, String> { } fn cut_bytes(reader: R, ranges: &[Range], opts: &Options) -> UResult<()> { - let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' }; + let newline_char = opts.line_ending.into(); let mut buf_in = BufReader::new(reader); let mut out = stdout_writer(); let delim = opts @@ -259,7 +260,7 @@ fn cut_fields_implicit_out_delim( } fn cut_fields(reader: R, ranges: &[Range], opts: &FieldOptions) -> UResult<()> { - let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' }; + let newline_char = opts.line_ending.into(); match opts.delimiter { Delimiter::String(ref delim) => { let matcher = ExactMatcher::new(delim.as_bytes()); @@ -376,7 +377,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .unwrap_or_default() .to_owned(), ), - zero_terminated: matches.get_flag(options::ZERO_TERMINATED), + line_ending: LineEnding::from(matches.get_flag(options::ZERO_TERMINATED)), }, ) }), @@ -391,7 +392,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .unwrap_or_default() .to_owned(), ), - zero_terminated: matches.get_flag(options::ZERO_TERMINATED), + line_ending: LineEnding::from(matches.get_flag(options::ZERO_TERMINATED)), }, ) }), @@ -411,6 +412,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let only_delimited = matches.get_flag(options::ONLY_DELIMITED); let whitespace_delimited = matches.get_flag(options::WHITESPACE_DELIMITED); let zero_terminated = matches.get_flag(options::ZERO_TERMINATED); + let line_ending = LineEnding::from(zero_terminated); match matches.get_one::(options::DELIMITER).map(|s| s.as_str()) { Some(_) if whitespace_delimited => { @@ -441,7 +443,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { delimiter: Delimiter::String(delim), out_delimiter: out_delim, only_delimited, - zero_terminated, + line_ending, }, )) } @@ -455,7 +457,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }, out_delimiter: out_delim, only_delimited, - zero_terminated, + line_ending, }, )), } diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index b293bc9bf66..f00cf695a8a 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -23,6 +23,7 @@ use std::os::unix::process::ExitStatusExt; use std::process; use uucore::display::Quotable; use uucore::error::{UClapError, UResult, USimpleError, UUsageError}; +use uucore::line_ending::LineEnding; use uucore::{format_usage, help_about, help_section, help_usage, show_warning}; const ABOUT: &str = help_about!("env.md"); @@ -31,7 +32,7 @@ const AFTER_HELP: &str = help_section!("after help", "env.md"); struct Options<'a> { ignore_env: bool, - null: bool, + line_ending: LineEnding, running_directory: Option<&'a str>, files: Vec<&'a str>, unsets: Vec<&'a str>, @@ -41,11 +42,11 @@ struct Options<'a> { // print name=value env pairs on screen // if null is true, separate pairs with a \0, \n otherwise -fn print_env(null: bool) { +fn print_env(line_ending: LineEnding) { let stdout_raw = io::stdout(); let mut stdout = stdout_raw.lock(); for (n, v) in env::vars() { - write!(stdout, "{}={}{}", n, v, if null { '\0' } else { '\n' }).unwrap(); + write!(stdout, "{}={}{}", n, v, line_ending).unwrap(); } } @@ -64,7 +65,7 @@ fn parse_name_value_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> UResult(opts: &mut Options<'a>, opt: &'a str) -> UResult<()> { - if opts.null { + if opts.line_ending == LineEnding::Nul { Err(UUsageError::new( 125, "cannot specify --null (-0) with command".to_string(), @@ -181,7 +182,7 @@ fn run_env(args: impl uucore::Args) -> UResult<()> { let matches = app.try_get_matches_from(args).with_exit_code(125)?; let ignore_env = matches.get_flag("ignore-environment"); - let null = matches.get_flag("null"); + let line_ending = LineEnding::from(matches.get_flag("null")); let running_directory = matches.get_one::("chdir").map(|s| s.as_str()); let files = match matches.get_many::("file") { Some(v) => v.map(|s| s.as_str()).collect(), @@ -194,7 +195,7 @@ fn run_env(args: impl uucore::Args) -> UResult<()> { let mut opts = Options { ignore_env, - null, + line_ending, running_directory, files, unsets, @@ -302,7 +303,7 @@ fn run_env(args: impl uucore::Args) -> UResult<()> { if opts.program.is_empty() { // no program provided, so just dump all env vars to stdout - print_env(opts.null); + print_env(opts.line_ending); } else { // we need to execute a command let (prog, args) = build_command(&mut opts.program); diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index 2517a98ea45..84eaab34278 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -6,6 +6,7 @@ // spell-checker:ignore (vars) zlines BUFWRITER seekable use clap::{crate_version, Arg, ArgAction, ArgMatches, Command}; +use uucore::line_ending::LineEnding; use std::ffi::OsString; use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write}; use uucore::display::Quotable; @@ -184,7 +185,7 @@ fn arg_iterate<'a>( struct HeadOptions { pub quiet: bool, pub verbose: bool, - pub zeroed: bool, + pub line_ending: LineEnding, pub presume_input_pipe: bool, pub mode: Mode, pub files: Vec, @@ -197,7 +198,7 @@ impl HeadOptions { options.quiet = matches.get_flag(options::QUIET_NAME); options.verbose = matches.get_flag(options::VERBOSE_NAME); - options.zeroed = matches.get_flag(options::ZERO_NAME); + options.line_ending = LineEnding::from(matches.get_flag(options::ZERO_NAME)); options.presume_input_pipe = matches.get_flag(options::PRESUME_INPUT_PIPE); options.mode = Mode::from(matches)?; @@ -227,9 +228,8 @@ where Ok(()) } -fn read_n_lines(input: &mut impl std::io::BufRead, n: u64, zero: bool) -> std::io::Result<()> { +fn read_n_lines(input: &mut impl std::io::BufRead, n: u64, separator: u8) -> std::io::Result<()> { // Read the first `n` lines from the `input` reader. - let separator = if zero { b'\0' } else { b'\n' }; let mut reader = take_lines(input, n, separator); // Write those bytes to `stdout`. @@ -293,20 +293,12 @@ fn read_but_last_n_bytes(input: &mut impl std::io::BufRead, n: usize) -> std::io fn read_but_last_n_lines( input: impl std::io::BufRead, n: usize, - zero: bool, + separator: u8, ) -> std::io::Result<()> { - if zero { - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - for bytes in take_all_but(lines(input, b'\0'), n) { - stdout.write_all(&bytes?)?; - } - } else { - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - for bytes in take_all_but(lines(input, b'\n'), n) { - stdout.write_all(&bytes?)?; - } + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + for bytes in take_all_but(lines(input, separator), n) { + stdout.write_all(&bytes?)?; } Ok(()) } @@ -350,7 +342,7 @@ fn read_but_last_n_lines( /// assert_eq!(find_nth_line_from_end(&mut input, 4, false).unwrap(), 0); /// assert_eq!(find_nth_line_from_end(&mut input, 1000, false).unwrap(), 0); /// ``` -fn find_nth_line_from_end(input: &mut R, n: u64, zeroed: bool) -> std::io::Result +fn find_nth_line_from_end(input: &mut R, n: u64, separator: u8) -> std::io::Result where R: Read + Seek, { @@ -370,14 +362,8 @@ where ))?; input.read_exact(buffer)?; for byte in buffer.iter().rev() { - match byte { - b'\n' if !zeroed => { - lines += 1; - } - 0u8 if zeroed => { - lines += 1; - } - _ => {} + if byte == &separator { + lines += 1; } // if it were just `n`, if lines == n + 1 { @@ -407,7 +393,7 @@ fn head_backwards_file(input: &mut std::fs::File, options: &HeadOptions) -> std: } } Mode::AllButLastLines(n) => { - let found = find_nth_line_from_end(input, n, options.zeroed)?; + let found = find_nth_line_from_end(input, n, options.line_ending.into())?; read_n_bytes( &mut std::io::BufReader::with_capacity(BUF_SIZE, input), found, @@ -426,7 +412,7 @@ fn head_file(input: &mut std::fs::File, options: &HeadOptions) -> std::io::Resul Mode::FirstLines(n) => read_n_lines( &mut std::io::BufReader::with_capacity(BUF_SIZE, input), n, - options.zeroed, + options.line_ending.into(), ), Mode::AllButLastBytes(_) | Mode::AllButLastLines(_) => head_backwards_file(input, options), } @@ -466,10 +452,10 @@ fn uu_head(options: &HeadOptions) -> UResult<()> { Mode::AllButLastBytes(n) => { read_but_last_n_bytes(&mut stdin, n.try_into().unwrap()) } - Mode::FirstLines(n) => read_n_lines(&mut stdin, n, options.zeroed), + Mode::FirstLines(n) => read_n_lines(&mut stdin, n, options.line_ending.into()), // unwrap is guaranteed to succeed because we checked the value of n above Mode::AllButLastLines(n) => { - read_but_last_n_lines(&mut stdin, n.try_into().unwrap(), options.zeroed) + read_but_last_n_lines(&mut stdin, n.try_into().unwrap(), options.line_ending.into()) } } } @@ -541,7 +527,7 @@ mod tests { #[test] fn test_args_modes() { let args = options("-n -10M -vz").unwrap(); - assert!(args.zeroed); + assert!(args.line_ending == LineEnding::Nul); assert!(args.verbose); assert_eq!(args.mode, Mode::AllButLastLines(10 * 1024 * 1024)); } @@ -561,8 +547,8 @@ mod tests { assert!(options("-q").unwrap().quiet); assert!(options("--verbose").unwrap().verbose); assert!(options("-v").unwrap().verbose); - assert!(options("--zero-terminated").unwrap().zeroed); - assert!(options("-z").unwrap().zeroed); + assert!(options("--zero-terminated").unwrap().line_ending == LineEnding::Nul); + assert!(options("-z").unwrap().line_ending == LineEnding::Nul); assert_eq!(options("--lines 15").unwrap().mode, Mode::FirstLines(15)); assert_eq!(options("-n 15").unwrap().mode, Mode::FirstLines(15)); assert_eq!(options("--bytes 15").unwrap().mode, Mode::FirstBytes(15)); @@ -579,7 +565,7 @@ mod tests { assert!(!opts.verbose); assert!(!opts.quiet); - assert!(!opts.zeroed); + assert!(opts.line_ending == LineEnding::Newline); assert_eq!(opts.mode, Mode::FirstLines(10)); assert!(opts.files.is_empty()); } @@ -631,17 +617,17 @@ mod tests { fn read_early_exit() { let mut empty = std::io::BufReader::new(std::io::Cursor::new(Vec::new())); assert!(read_n_bytes(&mut empty, 0).is_ok()); - assert!(read_n_lines(&mut empty, 0, false).is_ok()); + assert!(read_n_lines(&mut empty, 0, b'\n').is_ok()); } #[test] fn test_find_nth_line_from_end() { let mut input = Cursor::new("x\ny\nz\n"); - assert_eq!(find_nth_line_from_end(&mut input, 0, false).unwrap(), 6); - assert_eq!(find_nth_line_from_end(&mut input, 1, false).unwrap(), 4); - assert_eq!(find_nth_line_from_end(&mut input, 2, false).unwrap(), 2); - assert_eq!(find_nth_line_from_end(&mut input, 3, false).unwrap(), 0); - assert_eq!(find_nth_line_from_end(&mut input, 4, false).unwrap(), 0); - assert_eq!(find_nth_line_from_end(&mut input, 1000, false).unwrap(), 0); + assert_eq!(find_nth_line_from_end(&mut input, 0, b'\n').unwrap(), 6); + assert_eq!(find_nth_line_from_end(&mut input, 1, b'\n').unwrap(), 4); + assert_eq!(find_nth_line_from_end(&mut input, 2, b'\n').unwrap(), 2); + assert_eq!(find_nth_line_from_end(&mut input, 3, b'\n').unwrap(), 0); + assert_eq!(find_nth_line_from_end(&mut input, 4, b'\n').unwrap(), 0); + assert_eq!(find_nth_line_from_end(&mut input, 1000, b'\n').unwrap(), 0); } } diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 7a8e40059be..52b0750e0f7 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -44,6 +44,7 @@ use uucore::error::UResult; use uucore::error::{set_exit_code, USimpleError}; pub use uucore::libc; use uucore::libc::{getlogin, uid_t}; +use uucore::line_ending::LineEnding; use uucore::process::{getegid, geteuid, getgid, getuid}; use uucore::{format_usage, help_about, help_section, help_usage, show_error}; @@ -174,13 +175,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { " ".to_string() } }; - let line_ending = { - if state.zflag { - '\0' - } else { - '\n' - } - }; + let line_ending = LineEnding::from(state.zflag); if state.cflag { if state.selinux_supported { diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index cead745cd54..dc297bcb024 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -54,6 +54,7 @@ use unicode_width::UnicodeWidthStr; use uucore::libc::{dev_t, major, minor}; #[cfg(unix)] use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR}; +use uucore::line_ending::LineEnding; use uucore::quoting_style::{escape_name, QuotingStyle}; use uucore::{ display::Quotable, @@ -408,7 +409,7 @@ pub struct Config { context: bool, selinux_supported: bool, group_directories_first: bool, - eol: char, + line_ending: LineEnding, } // Fields that can be removed or added to the long format @@ -1005,11 +1006,7 @@ impl Config { } }, group_directories_first: options.get_flag(options::GROUP_DIRECTORIES_FIRST), - eol: if options.get_flag(options::ZERO) { - '\0' - } else { - '\n' - }, + line_ending: LineEnding::from(options.get_flag(options::ZERO)), }) } } @@ -2173,7 +2170,7 @@ fn display_total(items: &[PathData], config: &Config, out: &mut BufWriter 0 { - write!(out, "{}", config.eol)?; + write!(out, "{}", config.line_ending)?; } } _ => { for name in names { - write!(out, "{}{}", name.contents, config.eol)?; + write!(out, "{}{}", name.contents, config.line_ending)?; } } }; @@ -2491,7 +2488,7 @@ fn display_item_long( let dfn = display_file_name(item, config, None, String::new(), out).contents; - write!(out, " {} {}{}", display_date(md, config), dfn, config.eol)?; + write!(out, " {} {}{}", display_date(md, config), dfn, config.line_ending)?; } else { #[cfg(unix)] let leading_char = { diff --git a/src/uu/paste/src/paste.rs b/src/uu/paste/src/paste.rs index 6e52727f7ce..f5e95e821b5 100644 --- a/src/uu/paste/src/paste.rs +++ b/src/uu/paste/src/paste.rs @@ -8,11 +8,11 @@ // spell-checker:ignore (ToDO) delim use clap::{crate_version, Arg, ArgAction, Command}; -use std::fmt::Display; use std::fs::File; use std::io::{stdin, stdout, BufRead, BufReader, Read, Write}; use std::path::Path; use uucore::error::{FromIo, UResult, USimpleError}; +use uucore::line_ending::LineEnding; use uucore::{format_usage, help_about, help_usage}; const ABOUT: &str = help_about!("paste.md"); @@ -25,22 +25,6 @@ mod options { pub const ZERO_TERMINATED: &str = "zero-terminated"; } -#[repr(u8)] -#[derive(Clone, Copy)] -enum LineEnding { - Newline = b'\n', - Nul = 0, -} - -impl Display for LineEnding { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Newline => writeln!(f), - Self::Nul => write!(f, "\0"), - } - } -} - // Wraps BufReader and stdin fn read_until( reader: Option<&mut BufReader>, @@ -64,11 +48,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .unwrap() .map(|s| s.to_owned()) .collect(); - let line_ending = if matches.get_flag(options::ZERO_TERMINATED) { - LineEnding::Nul - } else { - LineEnding::Newline - }; + let line_ending = LineEnding::from(matches.get_flag(options::ZERO_TERMINATED)); paste(files, serial, delimiters, line_ending) } diff --git a/src/uu/readlink/src/readlink.rs b/src/uu/readlink/src/readlink.rs index e247c614695..2fbbabac10a 100644 --- a/src/uu/readlink/src/readlink.rs +++ b/src/uu/readlink/src/readlink.rs @@ -15,6 +15,7 @@ use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use uucore::{format_usage, help_about, help_usage, show_error}; +use uucore::line_ending::LineEnding; const ABOUT: &str = help_about!("readlink.md"); const USAGE: &str = help_usage!("readlink.md"); @@ -67,6 +68,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { show_error!("ignoring --no-newline with multiple arguments"); no_trailing_delimiter = false; } + let line_ending = if no_trailing_delimiter { + LineEnding::None + } else { + LineEnding::from(use_zero) + }; for f in &files { let p = PathBuf::from(f); @@ -77,7 +83,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; match path_result { Ok(path) => { - show(&path, no_trailing_delimiter, use_zero).map_err_context(String::new)?; + show(&path, line_ending).map_err_context(String::new)?; } Err(err) => { if verbose { @@ -173,14 +179,8 @@ pub fn uu_app() -> Command { ) } -fn show(path: &Path, no_trailing_delimiter: bool, use_zero: bool) -> std::io::Result<()> { +fn show(path: &Path, line_ending: LineEnding) -> std::io::Result<()> { let path = path.to_str().unwrap(); - if no_trailing_delimiter { - print!("{path}"); - } else if use_zero { - print!("{path}\0"); - } else { - println!("{path}"); - } + print!("{path}{line_ending}"); stdout().flush() } diff --git a/src/uu/realpath/src/realpath.rs b/src/uu/realpath/src/realpath.rs index cb7a09a41f3..6afd12787ff 100644 --- a/src/uu/realpath/src/realpath.rs +++ b/src/uu/realpath/src/realpath.rs @@ -21,6 +21,7 @@ use uucore::{ format_usage, fs::{canonicalize, MissingHandling, ResolveMode}, help_about, help_usage, + line_ending::LineEnding, }; use uucore::{error::UClapError, show, show_if_err}; @@ -52,7 +53,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .collect(); let strip = matches.get_flag(OPT_STRIP); - let zero = matches.get_flag(OPT_ZERO); + let line_ending = LineEnding::from(matches.get_flag(OPT_ZERO)); let quiet = matches.get_flag(OPT_QUIET); let logical = matches.get_flag(OPT_LOGICAL); let can_mode = if matches.get_flag(OPT_CANONICALIZE_EXISTING) { @@ -73,7 +74,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { for path in &paths { let result = resolve_path( path, - zero, + line_ending, resolve_mode, can_mode, relative_to.as_deref(), @@ -249,19 +250,18 @@ fn canonicalize_relative( /// symbolic links. fn resolve_path( p: &Path, - zero: bool, + line_ending: LineEnding, resolve: ResolveMode, can_mode: MissingHandling, relative_to: Option<&Path>, relative_base: Option<&Path>, ) -> std::io::Result<()> { let abs = canonicalize(p, can_mode, resolve)?; - let line_ending = if zero { b'\0' } else { b'\n' }; let abs = process_relative(abs, relative_base, relative_to); print_verbatim(abs)?; - stdout().write_all(&[line_ending])?; + stdout().write_all(&[line_ending.into()])?; Ok(()) } diff --git a/src/uu/sort/src/check.rs b/src/uu/sort/src/check.rs index 3f02a4c3153..700e3681ca4 100644 --- a/src/uu/sort/src/check.rs +++ b/src/uu/sort/src/check.rs @@ -115,11 +115,7 @@ fn reader( &mut carry_over, &mut file, &mut iter::empty(), - if settings.zero_terminated { - b'\0' - } else { - b'\n' - }, + settings.line_ending.into(), settings, )?; if !should_continue { diff --git a/src/uu/sort/src/ext_sort.rs b/src/uu/sort/src/ext_sort.rs index 27cb12d0bb0..a8f4b2590a3 100644 --- a/src/uu/sort/src/ext_sort.rs +++ b/src/uu/sort/src/ext_sort.rs @@ -84,11 +84,7 @@ fn reader_writer< output: Output, tmp_dir: &mut TmpDirWrapper, ) -> UResult<()> { - let separator = if settings.zero_terminated { - b'\0' - } else { - b'\n' - }; + let separator = settings.line_ending.into(); // Heuristically chosen: Dividing by 10 seems to keep our memory usage roughly // around settings.buffer_size as a whole. diff --git a/src/uu/sort/src/merge.rs b/src/uu/sort/src/merge.rs index 7c682d88f11..1a987fb4eec 100644 --- a/src/uu/sort/src/merge.rs +++ b/src/uu/sort/src/merge.rs @@ -169,11 +169,7 @@ fn merge_without_limit>>( &request_receiver, &mut reader_files, &settings, - if settings.zero_terminated { - b'\0' - } else { - b'\n' - }, + settings.line_ending.into(), ) } }); diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 80c2275e969..75277a42672 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -30,6 +30,7 @@ use fnv::FnvHasher; use numeric_str_cmp::{human_numeric_str_cmp, numeric_str_cmp, NumInfo, NumInfoParseSettings}; use rand::{thread_rng, Rng}; use rayon::prelude::*; +use uucore::line_ending::LineEnding; use std::cmp::Ordering; use std::env; use std::error::Error; @@ -306,7 +307,7 @@ pub struct GlobalSettings { selectors: Vec, separator: Option, threads: String, - zero_terminated: bool, + line_ending: LineEnding, buffer_size: usize, compress_prog: Option, merge_batch_size: usize, @@ -383,7 +384,7 @@ impl Default for GlobalSettings { selectors: vec![], separator: None, threads: String::new(), - zero_terminated: false, + line_ending: LineEnding::Newline, buffer_size: DEFAULT_BUF_SIZE, compress_prog: None, merge_batch_size: 32, @@ -526,12 +527,9 @@ impl<'a> Line<'a> { } fn print(&self, writer: &mut impl Write, settings: &GlobalSettings) { - if settings.zero_terminated && !settings.debug { + if !settings.debug { writer.write_all(self.line.as_bytes()).unwrap(); - writer.write_all(b"\0").unwrap(); - } else if !settings.debug { - writer.write_all(self.line.as_bytes()).unwrap(); - writer.write_all(b"\n").unwrap(); + writer.write(&[settings.line_ending.into()]).unwrap(); } else { self.print_debug(settings, writer).unwrap(); } @@ -1169,7 +1167,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { })?; } - settings.zero_terminated = matches.get_flag(options::ZERO_TERMINATED); + settings.line_ending = LineEnding::from(matches.get_flag(options::ZERO_TERMINATED)); settings.merge = matches.get_flag(options::MERGE); settings.check = matches.contains_id(options::check::CHECK); diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index ca9a48d258a..74fb9d4e3b2 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -22,6 +22,7 @@ pub use uucore_procs::*; pub use crate::mods::backup_control; pub use crate::mods::display; pub use crate::mods::error; +pub use crate::mods::line_ending; pub use crate::mods::os; pub use crate::mods::panic; pub use crate::mods::quoting_style; diff --git a/src/uucore/src/lib/mods.rs b/src/uucore/src/lib/mods.rs index 71d288c69a5..52da2788a48 100644 --- a/src/uucore/src/lib/mods.rs +++ b/src/uucore/src/lib/mods.rs @@ -3,6 +3,7 @@ pub mod backup_control; pub mod display; pub mod error; +pub mod line_ending; pub mod os; pub mod panic; pub mod ranges; diff --git a/src/uucore/src/lib/mods/line_ending.rs b/src/uucore/src/lib/mods/line_ending.rs new file mode 100644 index 00000000000..52d5e2f48d5 --- /dev/null +++ b/src/uucore/src/lib/mods/line_ending.rs @@ -0,0 +1,42 @@ +/// Aims to provide consistent newline/zero terminator handling +use std::fmt::Display; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +/// Line terminator used for printing and parsing +pub enum LineEnding { + #[default] Newline = b'\n', + Nul = 0, + Space = b' ', + None = 8, // abuse backspace \b to encode None +} + +impl Display for LineEnding { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Newline => writeln!(f), + Self::Nul => write!(f, "\0"), + Self::Space => write!(f, " "), + Self::None => std::fmt::Result::Ok(()), + } + } +} + +impl From for u8 { + fn from(line_ending: LineEnding) -> Self { + match line_ending { + LineEnding::None => panic!("Cannot convert LineEnding::None to u8"), + _ => line_ending as Self, + } + } +} + +impl From for LineEnding { + fn from(is_zero_terminated: bool) -> Self { + if is_zero_terminated { + Self::Nul + } else { + Self::Newline + } + } +}