mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
Extract uucore::line_ending::LineEnding (#5120)
* Extract uucore::line_ending::LineEnding Aims to provide consistent newline/zero terminator handling. * Apply suggestions from code review Co-authored-by: Terts Diepraam <terts.diepraam@gmail.com> * cargo fmt * Use uucore::line_ending::LineEnding * Remove uucore::line_ending::LineEnding::Space * Rename LineEnding::from_zero_flag * Replace LineEnding::None with Option<LineEnding> * cargo clippy * assert_eq * cargo clippy * cargo clippy * uucore/line_ending: add more documentation --------- Co-authored-by: Terts Diepraam <terts.diepraam@gmail.com>
This commit is contained in:
parent
3b0bfb10ac
commit
872818607f
20 changed files with 163 additions and 196 deletions
|
@ -11,6 +11,7 @@ use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
use std::path::{is_separator, PathBuf};
|
use std::path::{is_separator, PathBuf};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{UResult, UUsageError};
|
use uucore::error::{UResult, UUsageError};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::{format_usage, help_about, help_usage};
|
use uucore::{format_usage, help_about, help_usage};
|
||||||
|
|
||||||
static ABOUT: &str = help_about!("basename.md");
|
static ABOUT: &str = help_about!("basename.md");
|
||||||
|
@ -54,9 +55,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
return Err(UUsageError::new(1, "missing operand".to_string()));
|
return Err(UUsageError::new(1, "missing operand".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO));
|
||||||
|
|
||||||
let opt_suffix = matches.get_one::<String>(options::SUFFIX).is_some();
|
let opt_suffix = matches.get_one::<String>(options::SUFFIX).is_some();
|
||||||
let opt_multiple = matches.get_flag(options::MULTIPLE);
|
let opt_multiple = matches.get_flag(options::MULTIPLE);
|
||||||
let opt_zero = matches.get_flag(options::ZERO);
|
|
||||||
let multiple_paths = opt_suffix || opt_multiple;
|
let multiple_paths = opt_suffix || opt_multiple;
|
||||||
let name_args_count = matches
|
let name_args_count = matches
|
||||||
.get_many::<String>(options::NAME)
|
.get_many::<String>(options::NAME)
|
||||||
|
@ -105,7 +107,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let line_ending = if opt_zero { "\0" } else { "\n" };
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
print!("{}{}", basename(path, suffix), line_ending);
|
print!("{}{}", basename(path, suffix), line_ending);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
// spell-checker:ignore (ToDO) delim mkdelim
|
// spell-checker:ignore (ToDO) delim mkdelim
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt::Display;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, stdin, BufRead, BufReader, Stdin};
|
use std::io::{self, stdin, BufRead, BufReader, Stdin};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::error::{FromIo, UResult};
|
use uucore::error::{FromIo, UResult};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::{format_usage, help_about, help_usage};
|
use uucore::{format_usage, help_about, help_usage};
|
||||||
|
|
||||||
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
||||||
|
@ -40,38 +40,6 @@ fn column_width(col: &str, opts: &ArgMatches) -> usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum LineEnding {
|
|
||||||
Newline = b'\n',
|
|
||||||
Nul = 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LineEnding> for u8 {
|
|
||||||
fn from(line_ending: LineEnding) -> Self {
|
|
||||||
line_ending as Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> 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 {
|
enum Input {
|
||||||
Stdin(Stdin),
|
Stdin(Stdin),
|
||||||
FileIn(BufReader<File>),
|
FileIn(BufReader<File>),
|
||||||
|
@ -168,7 +136,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &ArgMatches) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.get_flag(options::TOTAL) {
|
if opts.get_flag(options::TOTAL) {
|
||||||
let line_ending = LineEnding::from(opts.get_flag(options::ZERO_TERMINATED));
|
let line_ending = LineEnding::from_zero_flag(opts.get_flag(options::ZERO_TERMINATED));
|
||||||
print!("{total_col_1}{delim}{total_col_2}{delim}{total_col_3}{delim}total{line_ending}");
|
print!("{total_col_1}{delim}{total_col_2}{delim}{total_col_3}{delim}total{line_ending}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +158,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let args = args.collect_lossy();
|
let args = args.collect_lossy();
|
||||||
|
|
||||||
let matches = uu_app().try_get_matches_from(args)?;
|
let matches = uu_app().try_get_matches_from(args)?;
|
||||||
let line_ending = LineEnding::from(matches.get_flag(options::ZERO_TERMINATED));
|
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO_TERMINATED));
|
||||||
let filename1 = matches.get_one::<String>(options::FILE_1).unwrap();
|
let filename1 = matches.get_one::<String>(options::FILE_1).unwrap();
|
||||||
let filename2 = matches.get_one::<String>(options::FILE_2).unwrap();
|
let filename2 = matches.get_one::<String>(options::FILE_2).unwrap();
|
||||||
let mut f1 = open_file(filename1, line_ending).map_err_context(|| filename1.to_string())?;
|
let mut f1 = open_file(filename1, line_ending).map_err_context(|| filename1.to_string())?;
|
||||||
|
|
|
@ -15,6 +15,7 @@ use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError};
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
|
|
||||||
use self::searcher::Searcher;
|
use self::searcher::Searcher;
|
||||||
use matcher::{ExactMatcher, Matcher, WhitespaceMatcher};
|
use matcher::{ExactMatcher, Matcher, WhitespaceMatcher};
|
||||||
|
@ -30,7 +31,7 @@ const AFTER_HELP: &str = help_section!("after help", "cut.md");
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
out_delim: Option<String>,
|
out_delim: Option<String>,
|
||||||
zero_terminated: bool,
|
line_ending: LineEnding,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Delimiter {
|
enum Delimiter {
|
||||||
|
@ -42,7 +43,7 @@ struct FieldOptions {
|
||||||
delimiter: Delimiter,
|
delimiter: Delimiter,
|
||||||
out_delimiter: Option<String>,
|
out_delimiter: Option<String>,
|
||||||
only_delimited: bool,
|
only_delimited: bool,
|
||||||
zero_terminated: bool,
|
line_ending: LineEnding,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Mode {
|
enum Mode {
|
||||||
|
@ -68,7 +69,7 @@ fn list_to_ranges(list: &str, complement: bool) -> Result<Vec<Range>, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> UResult<()> {
|
fn cut_bytes<R: Read>(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 buf_in = BufReader::new(reader);
|
||||||
let mut out = stdout_writer();
|
let mut out = stdout_writer();
|
||||||
let delim = opts
|
let delim = opts
|
||||||
|
@ -259,7 +260,7 @@ fn cut_fields_implicit_out_delim<R: Read, M: Matcher>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> UResult<()> {
|
fn cut_fields<R: Read>(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 {
|
match opts.delimiter {
|
||||||
Delimiter::String(ref delim) => {
|
Delimiter::String(ref delim) => {
|
||||||
let matcher = ExactMatcher::new(delim.as_bytes());
|
let matcher = ExactMatcher::new(delim.as_bytes());
|
||||||
|
@ -376,7 +377,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
zero_terminated: matches.get_flag(options::ZERO_TERMINATED),
|
line_ending: LineEnding::from_zero_flag(matches.get_flag(options::ZERO_TERMINATED)),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
@ -391,7 +392,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
zero_terminated: matches.get_flag(options::ZERO_TERMINATED),
|
line_ending: LineEnding::from_zero_flag(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 only_delimited = matches.get_flag(options::ONLY_DELIMITED);
|
||||||
let whitespace_delimited = matches.get_flag(options::WHITESPACE_DELIMITED);
|
let whitespace_delimited = matches.get_flag(options::WHITESPACE_DELIMITED);
|
||||||
let zero_terminated = matches.get_flag(options::ZERO_TERMINATED);
|
let zero_terminated = matches.get_flag(options::ZERO_TERMINATED);
|
||||||
|
let line_ending = LineEnding::from_zero_flag(zero_terminated);
|
||||||
|
|
||||||
match matches.get_one::<String>(options::DELIMITER).map(|s| s.as_str()) {
|
match matches.get_one::<String>(options::DELIMITER).map(|s| s.as_str()) {
|
||||||
Some(_) if whitespace_delimited => {
|
Some(_) if whitespace_delimited => {
|
||||||
|
@ -441,7 +443,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
delimiter: Delimiter::String(delim),
|
delimiter: Delimiter::String(delim),
|
||||||
out_delimiter: out_delim,
|
out_delimiter: out_delim,
|
||||||
only_delimited,
|
only_delimited,
|
||||||
zero_terminated,
|
line_ending,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -455,7 +457,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
},
|
},
|
||||||
out_delimiter: out_delim,
|
out_delimiter: out_delim,
|
||||||
only_delimited,
|
only_delimited,
|
||||||
zero_terminated,
|
line_ending,
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::display::print_verbatim;
|
use uucore::display::print_verbatim;
|
||||||
use uucore::error::{UResult, UUsageError};
|
use uucore::error::{UResult, UUsageError};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::{format_usage, help_about, help_section, help_usage};
|
use uucore::{format_usage, help_about, help_section, help_usage};
|
||||||
|
|
||||||
const ABOUT: &str = help_about!("dirname.md");
|
const ABOUT: &str = help_about!("dirname.md");
|
||||||
|
@ -26,11 +27,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
|
||||||
let matches = uu_app().after_help(AFTER_HELP).try_get_matches_from(args)?;
|
let matches = uu_app().after_help(AFTER_HELP).try_get_matches_from(args)?;
|
||||||
|
|
||||||
let separator = if matches.get_flag(options::ZERO) {
|
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO));
|
||||||
"\0"
|
|
||||||
} else {
|
|
||||||
"\n"
|
|
||||||
};
|
|
||||||
|
|
||||||
let dirnames: Vec<String> = matches
|
let dirnames: Vec<String> = matches
|
||||||
.get_many::<String>(options::DIR)
|
.get_many::<String>(options::DIR)
|
||||||
|
@ -59,7 +56,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print!("{separator}");
|
print!("{line_ending}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ use std::{error::Error, fmt::Display};
|
||||||
use uucore::display::{print_verbatim, Quotable};
|
use uucore::display::{print_verbatim, Quotable};
|
||||||
use uucore::error::FromIo;
|
use uucore::error::FromIo;
|
||||||
use uucore::error::{set_exit_code, UError, UResult};
|
use uucore::error::{set_exit_code, UError, UResult};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::parse_glob;
|
use uucore::parse_glob;
|
||||||
use uucore::parse_size::{parse_size, ParseSizeError};
|
use uucore::parse_size::{parse_size, ParseSizeError};
|
||||||
use uucore::{
|
use uucore::{
|
||||||
|
@ -600,11 +601,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let time_format_str =
|
let time_format_str =
|
||||||
parse_time_style(matches.get_one::<String>("time-style").map(|s| s.as_str()))?;
|
parse_time_style(matches.get_one::<String>("time-style").map(|s| s.as_str()))?;
|
||||||
|
|
||||||
let line_separator = if matches.get_flag(options::NULL) {
|
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::NULL));
|
||||||
"\0"
|
|
||||||
} else {
|
|
||||||
"\n"
|
|
||||||
};
|
|
||||||
|
|
||||||
let excludes = build_exclude_patterns(&matches)?;
|
let excludes = build_exclude_patterns(&matches)?;
|
||||||
|
|
||||||
|
@ -656,12 +653,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let time_str = tm.format(time_format_str).to_string();
|
let time_str = tm.format(time_format_str).to_string();
|
||||||
print!("{}\t{}\t", convert_size(size), time_str);
|
print!("{}\t{}\t", convert_size(size), time_str);
|
||||||
print_verbatim(stat.path).unwrap();
|
print_verbatim(stat.path).unwrap();
|
||||||
print!("{line_separator}");
|
print!("{line_ending}");
|
||||||
}
|
}
|
||||||
} else if !summarize || index == len - 1 {
|
} else if !summarize || index == len - 1 {
|
||||||
print!("{}\t", convert_size(size));
|
print!("{}\t", convert_size(size));
|
||||||
print_verbatim(stat.path).unwrap();
|
print_verbatim(stat.path).unwrap();
|
||||||
print!("{line_separator}");
|
print!("{line_ending}");
|
||||||
}
|
}
|
||||||
if options.total && index == (len - 1) {
|
if options.total && index == (len - 1) {
|
||||||
// The last element will be the total size of the the path under
|
// The last element will be the total size of the the path under
|
||||||
|
@ -681,7 +678,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
|
||||||
if options.total {
|
if options.total {
|
||||||
print!("{}\ttotal", convert_size(grand_total));
|
print!("{}\ttotal", convert_size(grand_total));
|
||||||
print!("{line_separator}");
|
print!("{line_ending}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
15
src/uu/env/src/env.rs
vendored
15
src/uu/env/src/env.rs
vendored
|
@ -23,6 +23,7 @@ use std::os::unix::process::ExitStatusExt;
|
||||||
use std::process;
|
use std::process;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{UClapError, UResult, USimpleError, UUsageError};
|
use uucore::error::{UClapError, UResult, USimpleError, UUsageError};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::{format_usage, help_about, help_section, help_usage, show_warning};
|
use uucore::{format_usage, help_about, help_section, help_usage, show_warning};
|
||||||
|
|
||||||
const ABOUT: &str = help_about!("env.md");
|
const ABOUT: &str = help_about!("env.md");
|
||||||
|
@ -31,7 +32,7 @@ const AFTER_HELP: &str = help_section!("after help", "env.md");
|
||||||
|
|
||||||
struct Options<'a> {
|
struct Options<'a> {
|
||||||
ignore_env: bool,
|
ignore_env: bool,
|
||||||
null: bool,
|
line_ending: LineEnding,
|
||||||
running_directory: Option<&'a str>,
|
running_directory: Option<&'a str>,
|
||||||
files: Vec<&'a str>,
|
files: Vec<&'a str>,
|
||||||
unsets: Vec<&'a str>,
|
unsets: Vec<&'a str>,
|
||||||
|
@ -41,11 +42,11 @@ struct Options<'a> {
|
||||||
|
|
||||||
// print name=value env pairs on screen
|
// print name=value env pairs on screen
|
||||||
// if null is true, separate pairs with a \0, \n otherwise
|
// 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 stdout_raw = io::stdout();
|
||||||
let mut stdout = stdout_raw.lock();
|
let mut stdout = stdout_raw.lock();
|
||||||
for (n, v) in env::vars() {
|
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<boo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_program_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> UResult<()> {
|
fn parse_program_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> UResult<()> {
|
||||||
if opts.null {
|
if opts.line_ending == LineEnding::Nul {
|
||||||
Err(UUsageError::new(
|
Err(UUsageError::new(
|
||||||
125,
|
125,
|
||||||
"cannot specify --null (-0) with command".to_string(),
|
"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 matches = app.try_get_matches_from(args).with_exit_code(125)?;
|
||||||
|
|
||||||
let ignore_env = matches.get_flag("ignore-environment");
|
let ignore_env = matches.get_flag("ignore-environment");
|
||||||
let null = matches.get_flag("null");
|
let line_ending = LineEnding::from_zero_flag(matches.get_flag("null"));
|
||||||
let running_directory = matches.get_one::<String>("chdir").map(|s| s.as_str());
|
let running_directory = matches.get_one::<String>("chdir").map(|s| s.as_str());
|
||||||
let files = match matches.get_many::<String>("file") {
|
let files = match matches.get_many::<String>("file") {
|
||||||
Some(v) => v.map(|s| s.as_str()).collect(),
|
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 {
|
let mut opts = Options {
|
||||||
ignore_env,
|
ignore_env,
|
||||||
null,
|
line_ending,
|
||||||
running_directory,
|
running_directory,
|
||||||
files,
|
files,
|
||||||
unsets,
|
unsets,
|
||||||
|
@ -302,7 +303,7 @@ fn run_env(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
|
||||||
if opts.program.is_empty() {
|
if opts.program.is_empty() {
|
||||||
// no program provided, so just dump all env vars to stdout
|
// no program provided, so just dump all env vars to stdout
|
||||||
print_env(opts.null);
|
print_env(opts.line_ending);
|
||||||
} else {
|
} else {
|
||||||
// we need to execute a command
|
// we need to execute a command
|
||||||
let (prog, args) = build_command(&mut opts.program);
|
let (prog, args) = build_command(&mut opts.program);
|
||||||
|
|
|
@ -10,6 +10,7 @@ use std::ffi::OsString;
|
||||||
use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write};
|
use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UError, UResult, USimpleError};
|
use uucore::error::{FromIo, UError, UResult, USimpleError};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::lines::lines;
|
use uucore::lines::lines;
|
||||||
use uucore::{format_usage, help_about, help_usage, show};
|
use uucore::{format_usage, help_about, help_usage, show};
|
||||||
|
|
||||||
|
@ -184,7 +185,7 @@ fn arg_iterate<'a>(
|
||||||
struct HeadOptions {
|
struct HeadOptions {
|
||||||
pub quiet: bool,
|
pub quiet: bool,
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
pub zeroed: bool,
|
pub line_ending: LineEnding,
|
||||||
pub presume_input_pipe: bool,
|
pub presume_input_pipe: bool,
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub files: Vec<String>,
|
pub files: Vec<String>,
|
||||||
|
@ -197,7 +198,7 @@ impl HeadOptions {
|
||||||
|
|
||||||
options.quiet = matches.get_flag(options::QUIET_NAME);
|
options.quiet = matches.get_flag(options::QUIET_NAME);
|
||||||
options.verbose = matches.get_flag(options::VERBOSE_NAME);
|
options.verbose = matches.get_flag(options::VERBOSE_NAME);
|
||||||
options.zeroed = matches.get_flag(options::ZERO_NAME);
|
options.line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO_NAME));
|
||||||
options.presume_input_pipe = matches.get_flag(options::PRESUME_INPUT_PIPE);
|
options.presume_input_pipe = matches.get_flag(options::PRESUME_INPUT_PIPE);
|
||||||
|
|
||||||
options.mode = Mode::from(matches)?;
|
options.mode = Mode::from(matches)?;
|
||||||
|
@ -227,9 +228,8 @@ where
|
||||||
Ok(())
|
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.
|
// 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);
|
let mut reader = take_lines(input, n, separator);
|
||||||
|
|
||||||
// Write those bytes to `stdout`.
|
// 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(
|
fn read_but_last_n_lines(
|
||||||
input: impl std::io::BufRead,
|
input: impl std::io::BufRead,
|
||||||
n: usize,
|
n: usize,
|
||||||
zero: bool,
|
separator: u8,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
if zero {
|
let stdout = std::io::stdout();
|
||||||
let stdout = std::io::stdout();
|
let mut stdout = stdout.lock();
|
||||||
let mut stdout = stdout.lock();
|
for bytes in take_all_but(lines(input, separator), n) {
|
||||||
for bytes in take_all_but(lines(input, b'\0'), n) {
|
stdout.write_all(&bytes?)?;
|
||||||
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?)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
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, 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, 1000, false).unwrap(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
fn find_nth_line_from_end<R>(input: &mut R, n: u64, zeroed: bool) -> std::io::Result<u64>
|
fn find_nth_line_from_end<R>(input: &mut R, n: u64, separator: u8) -> std::io::Result<u64>
|
||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
{
|
{
|
||||||
|
@ -370,14 +362,8 @@ where
|
||||||
))?;
|
))?;
|
||||||
input.read_exact(buffer)?;
|
input.read_exact(buffer)?;
|
||||||
for byte in buffer.iter().rev() {
|
for byte in buffer.iter().rev() {
|
||||||
match byte {
|
if byte == &separator {
|
||||||
b'\n' if !zeroed => {
|
lines += 1;
|
||||||
lines += 1;
|
|
||||||
}
|
|
||||||
0u8 if zeroed => {
|
|
||||||
lines += 1;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
// if it were just `n`,
|
// if it were just `n`,
|
||||||
if lines == n + 1 {
|
if lines == n + 1 {
|
||||||
|
@ -407,7 +393,7 @@ fn head_backwards_file(input: &mut std::fs::File, options: &HeadOptions) -> std:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mode::AllButLastLines(n) => {
|
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(
|
read_n_bytes(
|
||||||
&mut std::io::BufReader::with_capacity(BUF_SIZE, input),
|
&mut std::io::BufReader::with_capacity(BUF_SIZE, input),
|
||||||
found,
|
found,
|
||||||
|
@ -426,7 +412,7 @@ fn head_file(input: &mut std::fs::File, options: &HeadOptions) -> std::io::Resul
|
||||||
Mode::FirstLines(n) => read_n_lines(
|
Mode::FirstLines(n) => read_n_lines(
|
||||||
&mut std::io::BufReader::with_capacity(BUF_SIZE, input),
|
&mut std::io::BufReader::with_capacity(BUF_SIZE, input),
|
||||||
n,
|
n,
|
||||||
options.zeroed,
|
options.line_ending.into(),
|
||||||
),
|
),
|
||||||
Mode::AllButLastBytes(_) | Mode::AllButLastLines(_) => head_backwards_file(input, options),
|
Mode::AllButLastBytes(_) | Mode::AllButLastLines(_) => head_backwards_file(input, options),
|
||||||
}
|
}
|
||||||
|
@ -466,11 +452,13 @@ fn uu_head(options: &HeadOptions) -> UResult<()> {
|
||||||
Mode::AllButLastBytes(n) => {
|
Mode::AllButLastBytes(n) => {
|
||||||
read_but_last_n_bytes(&mut stdin, n.try_into().unwrap())
|
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
|
// unwrap is guaranteed to succeed because we checked the value of n above
|
||||||
Mode::AllButLastLines(n) => {
|
Mode::AllButLastLines(n) => read_but_last_n_lines(
|
||||||
read_but_last_n_lines(&mut stdin, n.try_into().unwrap(), options.zeroed)
|
&mut stdin,
|
||||||
}
|
n.try_into().unwrap(),
|
||||||
|
options.line_ending.into(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(name, false) => {
|
(name, false) => {
|
||||||
|
@ -541,7 +529,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_args_modes() {
|
fn test_args_modes() {
|
||||||
let args = options("-n -10M -vz").unwrap();
|
let args = options("-n -10M -vz").unwrap();
|
||||||
assert!(args.zeroed);
|
assert_eq!(args.line_ending, LineEnding::Nul);
|
||||||
assert!(args.verbose);
|
assert!(args.verbose);
|
||||||
assert_eq!(args.mode, Mode::AllButLastLines(10 * 1024 * 1024));
|
assert_eq!(args.mode, Mode::AllButLastLines(10 * 1024 * 1024));
|
||||||
}
|
}
|
||||||
|
@ -561,8 +549,11 @@ mod tests {
|
||||||
assert!(options("-q").unwrap().quiet);
|
assert!(options("-q").unwrap().quiet);
|
||||||
assert!(options("--verbose").unwrap().verbose);
|
assert!(options("--verbose").unwrap().verbose);
|
||||||
assert!(options("-v").unwrap().verbose);
|
assert!(options("-v").unwrap().verbose);
|
||||||
assert!(options("--zero-terminated").unwrap().zeroed);
|
assert_eq!(
|
||||||
assert!(options("-z").unwrap().zeroed);
|
options("--zero-terminated").unwrap().line_ending,
|
||||||
|
LineEnding::Nul
|
||||||
|
);
|
||||||
|
assert_eq!(options("-z").unwrap().line_ending, LineEnding::Nul);
|
||||||
assert_eq!(options("--lines 15").unwrap().mode, Mode::FirstLines(15));
|
assert_eq!(options("--lines 15").unwrap().mode, Mode::FirstLines(15));
|
||||||
assert_eq!(options("-n 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));
|
assert_eq!(options("--bytes 15").unwrap().mode, Mode::FirstBytes(15));
|
||||||
|
@ -579,7 +570,7 @@ mod tests {
|
||||||
|
|
||||||
assert!(!opts.verbose);
|
assert!(!opts.verbose);
|
||||||
assert!(!opts.quiet);
|
assert!(!opts.quiet);
|
||||||
assert!(!opts.zeroed);
|
assert_eq!(opts.line_ending, LineEnding::Newline);
|
||||||
assert_eq!(opts.mode, Mode::FirstLines(10));
|
assert_eq!(opts.mode, Mode::FirstLines(10));
|
||||||
assert!(opts.files.is_empty());
|
assert!(opts.files.is_empty());
|
||||||
}
|
}
|
||||||
|
@ -631,17 +622,17 @@ mod tests {
|
||||||
fn read_early_exit() {
|
fn read_early_exit() {
|
||||||
let mut empty = std::io::BufReader::new(std::io::Cursor::new(Vec::new()));
|
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_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]
|
#[test]
|
||||||
fn test_find_nth_line_from_end() {
|
fn test_find_nth_line_from_end() {
|
||||||
let mut input = Cursor::new("x\ny\nz\n");
|
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, 0, b'\n').unwrap(), 6);
|
||||||
assert_eq!(find_nth_line_from_end(&mut input, 1, false).unwrap(), 4);
|
assert_eq!(find_nth_line_from_end(&mut input, 1, b'\n').unwrap(), 4);
|
||||||
assert_eq!(find_nth_line_from_end(&mut input, 2, false).unwrap(), 2);
|
assert_eq!(find_nth_line_from_end(&mut input, 2, b'\n').unwrap(), 2);
|
||||||
assert_eq!(find_nth_line_from_end(&mut input, 3, false).unwrap(), 0);
|
assert_eq!(find_nth_line_from_end(&mut input, 3, b'\n').unwrap(), 0);
|
||||||
assert_eq!(find_nth_line_from_end(&mut input, 4, false).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, false).unwrap(), 0);
|
assert_eq!(find_nth_line_from_end(&mut input, 1000, b'\n').unwrap(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ use uucore::error::UResult;
|
||||||
use uucore::error::{set_exit_code, USimpleError};
|
use uucore::error::{set_exit_code, USimpleError};
|
||||||
pub use uucore::libc;
|
pub use uucore::libc;
|
||||||
use uucore::libc::{getlogin, uid_t};
|
use uucore::libc::{getlogin, uid_t};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::process::{getegid, geteuid, getgid, getuid};
|
use uucore::process::{getegid, geteuid, getgid, getuid};
|
||||||
use uucore::{format_usage, help_about, help_section, help_usage, show_error};
|
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()
|
" ".to_string()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let line_ending = {
|
let line_ending = LineEnding::from_zero_flag(state.zflag);
|
||||||
if state.zflag {
|
|
||||||
'\0'
|
|
||||||
} else {
|
|
||||||
'\n'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if state.cflag {
|
if state.cflag {
|
||||||
if state.selinux_supported {
|
if state.selinux_supported {
|
||||||
|
|
|
@ -22,6 +22,7 @@ use std::num::IntErrorKind;
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{set_exit_code, UError, UResult, USimpleError};
|
use uucore::error::{set_exit_code, UError, UResult, USimpleError};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::{crash, crash_if_err, format_usage, help_about, help_usage};
|
use uucore::{crash, crash_if_err, format_usage, help_about, help_usage};
|
||||||
|
|
||||||
const ABOUT: &str = help_about!("join.md");
|
const ABOUT: &str = help_about!("join.md");
|
||||||
|
@ -62,13 +63,6 @@ enum FileNum {
|
||||||
File2,
|
File2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
enum LineEnding {
|
|
||||||
Nul = 0,
|
|
||||||
Newline = b'\n',
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
enum Sep {
|
enum Sep {
|
||||||
Char(u8),
|
Char(u8),
|
||||||
|
@ -683,9 +677,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
settings.headers = true;
|
settings.headers = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches.get_flag("z") {
|
settings.line_ending = LineEnding::from_zero_flag(matches.get_flag("z"));
|
||||||
settings.line_ending = LineEnding::Nul;
|
|
||||||
}
|
|
||||||
|
|
||||||
let file1 = matches.get_one::<String>("file1").unwrap();
|
let file1 = matches.get_one::<String>("file1").unwrap();
|
||||||
let file2 = matches.get_one::<String>("file2").unwrap();
|
let file2 = matches.get_one::<String>("file2").unwrap();
|
||||||
|
|
|
@ -54,6 +54,7 @@ use unicode_width::UnicodeWidthStr;
|
||||||
use uucore::libc::{dev_t, major, minor};
|
use uucore::libc::{dev_t, major, minor};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
|
use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::quoting_style::{escape_name, QuotingStyle};
|
use uucore::quoting_style::{escape_name, QuotingStyle};
|
||||||
use uucore::{
|
use uucore::{
|
||||||
display::Quotable,
|
display::Quotable,
|
||||||
|
@ -408,7 +409,7 @@ pub struct Config {
|
||||||
context: bool,
|
context: bool,
|
||||||
selinux_supported: bool,
|
selinux_supported: bool,
|
||||||
group_directories_first: bool,
|
group_directories_first: bool,
|
||||||
eol: char,
|
line_ending: LineEnding,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields that can be removed or added to the long format
|
// 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),
|
group_directories_first: options.get_flag(options::GROUP_DIRECTORIES_FIRST),
|
||||||
eol: if options.get_flag(options::ZERO) {
|
line_ending: LineEnding::from_zero_flag(options.get_flag(options::ZERO)),
|
||||||
'\0'
|
|
||||||
} else {
|
|
||||||
'\n'
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2173,7 +2170,7 @@ fn display_total(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
|
||||||
out,
|
out,
|
||||||
"total {}{}",
|
"total {}{}",
|
||||||
display_size(total_size, config),
|
display_size(total_size, config),
|
||||||
config.eol
|
config.line_ending
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2285,12 +2282,12 @@ fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
|
||||||
// Current col is never zero again if names have been printed.
|
// Current col is never zero again if names have been printed.
|
||||||
// So we print a newline.
|
// So we print a newline.
|
||||||
if current_col > 0 {
|
if current_col > 0 {
|
||||||
write!(out, "{}", config.eol)?;
|
write!(out, "{}", config.line_ending)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
for name in names {
|
for name in names {
|
||||||
write!(out, "{}{}", name.contents, config.eol)?;
|
write!(out, "{}{}", name.contents, config.line_ending)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2491,7 +2488,13 @@ fn display_item_long(
|
||||||
|
|
||||||
let dfn = display_file_name(item, config, None, String::new(), out).contents;
|
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 {
|
} else {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let leading_char = {
|
let leading_char = {
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
// spell-checker:ignore (ToDO) delim
|
// spell-checker:ignore (ToDO) delim
|
||||||
|
|
||||||
use clap::{crate_version, Arg, ArgAction, Command};
|
use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
use std::fmt::Display;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{stdin, stdout, BufRead, BufReader, Read, Write};
|
use std::io::{stdin, stdout, BufRead, BufReader, Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError};
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::{format_usage, help_about, help_usage};
|
use uucore::{format_usage, help_about, help_usage};
|
||||||
|
|
||||||
const ABOUT: &str = help_about!("paste.md");
|
const ABOUT: &str = help_about!("paste.md");
|
||||||
|
@ -25,22 +25,6 @@ mod options {
|
||||||
pub const ZERO_TERMINATED: &str = "zero-terminated";
|
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
|
// Wraps BufReader and stdin
|
||||||
fn read_until<R: Read>(
|
fn read_until<R: Read>(
|
||||||
reader: Option<&mut BufReader<R>>,
|
reader: Option<&mut BufReader<R>>,
|
||||||
|
@ -64,11 +48,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(|s| s.to_owned())
|
.map(|s| s.to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
let line_ending = if matches.get_flag(options::ZERO_TERMINATED) {
|
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO_TERMINATED));
|
||||||
LineEnding::Nul
|
|
||||||
} else {
|
|
||||||
LineEnding::Newline
|
|
||||||
};
|
|
||||||
|
|
||||||
paste(files, serial, delimiters, line_ending)
|
paste(files, serial, delimiters, line_ending)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ use std::path::{Path, PathBuf};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
|
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
|
||||||
use uucore::fs::{canonicalize, MissingHandling, ResolveMode};
|
use uucore::fs::{canonicalize, MissingHandling, ResolveMode};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::{format_usage, help_about, help_usage, show_error};
|
use uucore::{format_usage, help_about, help_usage, show_error};
|
||||||
|
|
||||||
const ABOUT: &str = help_about!("readlink.md");
|
const ABOUT: &str = help_about!("readlink.md");
|
||||||
|
@ -67,6 +68,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
show_error!("ignoring --no-newline with multiple arguments");
|
show_error!("ignoring --no-newline with multiple arguments");
|
||||||
no_trailing_delimiter = false;
|
no_trailing_delimiter = false;
|
||||||
}
|
}
|
||||||
|
let line_ending = if no_trailing_delimiter {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(LineEnding::from_zero_flag(use_zero))
|
||||||
|
};
|
||||||
|
|
||||||
for f in &files {
|
for f in &files {
|
||||||
let p = PathBuf::from(f);
|
let p = PathBuf::from(f);
|
||||||
|
@ -77,7 +83,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
};
|
};
|
||||||
match path_result {
|
match path_result {
|
||||||
Ok(path) => {
|
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) => {
|
Err(err) => {
|
||||||
if verbose {
|
if verbose {
|
||||||
|
@ -173,14 +179,11 @@ 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: Option<LineEnding>) -> std::io::Result<()> {
|
||||||
let path = path.to_str().unwrap();
|
let path = path.to_str().unwrap();
|
||||||
if no_trailing_delimiter {
|
print!("{path}");
|
||||||
print!("{path}");
|
if let Some(line_ending) = line_ending {
|
||||||
} else if use_zero {
|
print!("{line_ending}");
|
||||||
print!("{path}\0");
|
|
||||||
} else {
|
|
||||||
println!("{path}");
|
|
||||||
}
|
}
|
||||||
stdout().flush()
|
stdout().flush()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ use uucore::{
|
||||||
format_usage,
|
format_usage,
|
||||||
fs::{canonicalize, MissingHandling, ResolveMode},
|
fs::{canonicalize, MissingHandling, ResolveMode},
|
||||||
help_about, help_usage,
|
help_about, help_usage,
|
||||||
|
line_ending::LineEnding,
|
||||||
};
|
};
|
||||||
use uucore::{error::UClapError, show, show_if_err};
|
use uucore::{error::UClapError, show, show_if_err};
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let strip = matches.get_flag(OPT_STRIP);
|
let strip = matches.get_flag(OPT_STRIP);
|
||||||
let zero = matches.get_flag(OPT_ZERO);
|
let line_ending = LineEnding::from_zero_flag(matches.get_flag(OPT_ZERO));
|
||||||
let quiet = matches.get_flag(OPT_QUIET);
|
let quiet = matches.get_flag(OPT_QUIET);
|
||||||
let logical = matches.get_flag(OPT_LOGICAL);
|
let logical = matches.get_flag(OPT_LOGICAL);
|
||||||
let can_mode = if matches.get_flag(OPT_CANONICALIZE_EXISTING) {
|
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 {
|
for path in &paths {
|
||||||
let result = resolve_path(
|
let result = resolve_path(
|
||||||
path,
|
path,
|
||||||
zero,
|
line_ending,
|
||||||
resolve_mode,
|
resolve_mode,
|
||||||
can_mode,
|
can_mode,
|
||||||
relative_to.as_deref(),
|
relative_to.as_deref(),
|
||||||
|
@ -249,19 +250,18 @@ fn canonicalize_relative(
|
||||||
/// symbolic links.
|
/// symbolic links.
|
||||||
fn resolve_path(
|
fn resolve_path(
|
||||||
p: &Path,
|
p: &Path,
|
||||||
zero: bool,
|
line_ending: LineEnding,
|
||||||
resolve: ResolveMode,
|
resolve: ResolveMode,
|
||||||
can_mode: MissingHandling,
|
can_mode: MissingHandling,
|
||||||
relative_to: Option<&Path>,
|
relative_to: Option<&Path>,
|
||||||
relative_base: Option<&Path>,
|
relative_base: Option<&Path>,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
let abs = canonicalize(p, can_mode, resolve)?;
|
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);
|
let abs = process_relative(abs, relative_base, relative_to);
|
||||||
|
|
||||||
print_verbatim(abs)?;
|
print_verbatim(abs)?;
|
||||||
stdout().write_all(&[line_ending])?;
|
stdout().write_all(&[line_ending.into()])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,11 +115,7 @@ fn reader(
|
||||||
&mut carry_over,
|
&mut carry_over,
|
||||||
&mut file,
|
&mut file,
|
||||||
&mut iter::empty(),
|
&mut iter::empty(),
|
||||||
if settings.zero_terminated {
|
settings.line_ending.into(),
|
||||||
b'\0'
|
|
||||||
} else {
|
|
||||||
b'\n'
|
|
||||||
},
|
|
||||||
settings,
|
settings,
|
||||||
)?;
|
)?;
|
||||||
if !should_continue {
|
if !should_continue {
|
||||||
|
|
|
@ -84,11 +84,7 @@ fn reader_writer<
|
||||||
output: Output,
|
output: Output,
|
||||||
tmp_dir: &mut TmpDirWrapper,
|
tmp_dir: &mut TmpDirWrapper,
|
||||||
) -> UResult<()> {
|
) -> UResult<()> {
|
||||||
let separator = if settings.zero_terminated {
|
let separator = settings.line_ending.into();
|
||||||
b'\0'
|
|
||||||
} else {
|
|
||||||
b'\n'
|
|
||||||
};
|
|
||||||
|
|
||||||
// Heuristically chosen: Dividing by 10 seems to keep our memory usage roughly
|
// Heuristically chosen: Dividing by 10 seems to keep our memory usage roughly
|
||||||
// around settings.buffer_size as a whole.
|
// around settings.buffer_size as a whole.
|
||||||
|
|
|
@ -169,11 +169,7 @@ fn merge_without_limit<M: MergeInput + 'static, F: Iterator<Item = UResult<M>>>(
|
||||||
&request_receiver,
|
&request_receiver,
|
||||||
&mut reader_files,
|
&mut reader_files,
|
||||||
&settings,
|
&settings,
|
||||||
if settings.zero_terminated {
|
settings.line_ending.into(),
|
||||||
b'\0'
|
|
||||||
} else {
|
|
||||||
b'\n'
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -45,6 +45,7 @@ use std::str::Utf8Error;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{set_exit_code, strip_errno, UError, UResult, USimpleError, UUsageError};
|
use uucore::error::{set_exit_code, strip_errno, UError, UResult, USimpleError, UUsageError};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::parse_size::{ParseSizeError, Parser};
|
use uucore::parse_size::{ParseSizeError, Parser};
|
||||||
use uucore::version_cmp::version_cmp;
|
use uucore::version_cmp::version_cmp;
|
||||||
use uucore::{format_usage, help_about, help_section, help_usage};
|
use uucore::{format_usage, help_about, help_section, help_usage};
|
||||||
|
@ -306,7 +307,7 @@ pub struct GlobalSettings {
|
||||||
selectors: Vec<FieldSelector>,
|
selectors: Vec<FieldSelector>,
|
||||||
separator: Option<char>,
|
separator: Option<char>,
|
||||||
threads: String,
|
threads: String,
|
||||||
zero_terminated: bool,
|
line_ending: LineEnding,
|
||||||
buffer_size: usize,
|
buffer_size: usize,
|
||||||
compress_prog: Option<String>,
|
compress_prog: Option<String>,
|
||||||
merge_batch_size: usize,
|
merge_batch_size: usize,
|
||||||
|
@ -383,7 +384,7 @@ impl Default for GlobalSettings {
|
||||||
selectors: vec![],
|
selectors: vec![],
|
||||||
separator: None,
|
separator: None,
|
||||||
threads: String::new(),
|
threads: String::new(),
|
||||||
zero_terminated: false,
|
line_ending: LineEnding::Newline,
|
||||||
buffer_size: DEFAULT_BUF_SIZE,
|
buffer_size: DEFAULT_BUF_SIZE,
|
||||||
compress_prog: None,
|
compress_prog: None,
|
||||||
merge_batch_size: 32,
|
merge_batch_size: 32,
|
||||||
|
@ -526,14 +527,11 @@ impl<'a> Line<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print(&self, writer: &mut impl Write, settings: &GlobalSettings) {
|
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();
|
|
||||||
} else {
|
|
||||||
self.print_debug(settings, writer).unwrap();
|
self.print_debug(settings, writer).unwrap();
|
||||||
|
} else {
|
||||||
|
writer.write_all(self.line.as_bytes()).unwrap();
|
||||||
|
writer.write_all(&[settings.line_ending.into()]).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_zero_flag(matches.get_flag(options::ZERO_TERMINATED));
|
||||||
settings.merge = matches.get_flag(options::MERGE);
|
settings.merge = matches.get_flag(options::MERGE);
|
||||||
|
|
||||||
settings.check = matches.contains_id(options::check::CHECK);
|
settings.check = matches.contains_id(options::check::CHECK);
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub use uucore_procs::*;
|
||||||
pub use crate::mods::backup_control;
|
pub use crate::mods::backup_control;
|
||||||
pub use crate::mods::display;
|
pub use crate::mods::display;
|
||||||
pub use crate::mods::error;
|
pub use crate::mods::error;
|
||||||
|
pub use crate::mods::line_ending;
|
||||||
pub use crate::mods::os;
|
pub use crate::mods::os;
|
||||||
pub use crate::mods::panic;
|
pub use crate::mods::panic;
|
||||||
pub use crate::mods::quoting_style;
|
pub use crate::mods::quoting_style;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
pub mod backup_control;
|
pub mod backup_control;
|
||||||
pub mod display;
|
pub mod display;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod line_ending;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
pub mod panic;
|
pub mod panic;
|
||||||
pub mod ranges;
|
pub mod ranges;
|
||||||
|
|
49
src/uucore/src/lib/mods/line_ending.rs
Normal file
49
src/uucore/src/lib/mods/line_ending.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
//! Provides consistent newline/zero terminator handling for `-z`/`--zero` flags.
|
||||||
|
//!
|
||||||
|
//! See the [`LineEnding`] struct for more information.
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
/// Line ending of either `\n` or `\0`
|
||||||
|
///
|
||||||
|
/// Used by various utilities that have the option to separate lines by nul
|
||||||
|
/// characters instead of `\n`. Usually, this is specified with the `-z` or
|
||||||
|
/// `--zero` flag.
|
||||||
|
///
|
||||||
|
/// The [`Display`] implementation writes the character corresponding to the
|
||||||
|
/// variant to the formatter.
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub enum LineEnding {
|
||||||
|
#[default]
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LineEnding> for u8 {
|
||||||
|
fn from(line_ending: LineEnding) -> Self {
|
||||||
|
line_ending as Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineEnding {
|
||||||
|
/// Create a [`LineEnding`] from a `-z`/`--zero` flag
|
||||||
|
///
|
||||||
|
/// If `is_zero_terminated` is true, [`LineEnding::Nul`] is returned,
|
||||||
|
/// otherwise [`LineEnding::Newline`].
|
||||||
|
pub fn from_zero_flag(is_zero_terminated: bool) -> Self {
|
||||||
|
if is_zero_terminated {
|
||||||
|
Self::Nul
|
||||||
|
} else {
|
||||||
|
Self::Newline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue