mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 19:47:45 +00:00
Merge pull request #4911 from jeddenlea/uniq
uniq: non-UTF-8 file names and fewer copies
This commit is contained in:
commit
f89182e89b
1 changed files with 37 additions and 46 deletions
|
@ -5,10 +5,10 @@
|
||||||
// * For the full copyright and license information, please view the LICENSE
|
// * For the full copyright and license information, please view the LICENSE
|
||||||
// * file that was distributed with this source code.
|
// * file that was distributed with this source code.
|
||||||
|
|
||||||
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
use clap::{builder::ValueParser, crate_version, Arg, ArgAction, ArgMatches, Command};
|
||||||
|
use std::ffi::{OsStr, OsString};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
|
use std::io::{self, stdin, stdout, BufRead, BufReader, BufWriter, Write};
|
||||||
use std::path::Path;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
|
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
|
||||||
|
@ -64,11 +64,7 @@ macro_rules! write_line_terminator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Uniq {
|
impl Uniq {
|
||||||
pub fn print_uniq<R: Read, W: Write>(
|
pub fn print_uniq(&self, reader: impl BufRead, mut writer: impl Write) -> UResult<()> {
|
||||||
&self,
|
|
||||||
reader: &mut BufReader<R>,
|
|
||||||
writer: &mut BufWriter<W>,
|
|
||||||
) -> UResult<()> {
|
|
||||||
let mut first_line_printed = false;
|
let mut first_line_printed = false;
|
||||||
let mut group_count = 1;
|
let mut group_count = 1;
|
||||||
let line_terminator = self.get_line_terminator();
|
let line_terminator = self.get_line_terminator();
|
||||||
|
@ -78,6 +74,8 @@ impl Uniq {
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let writer = &mut writer;
|
||||||
|
|
||||||
// compare current `line` with consecutive lines (`next_line`) of the input
|
// compare current `line` with consecutive lines (`next_line`) of the input
|
||||||
// and if needed, print `line` based on the command line options provided
|
// and if needed, print `line` based on the command line options provided
|
||||||
for next_line in lines {
|
for next_line in lines {
|
||||||
|
@ -122,7 +120,6 @@ impl Uniq {
|
||||||
}
|
}
|
||||||
match char_indices.find(|(_, c)| c.is_whitespace()) {
|
match char_indices.find(|(_, c)| c.is_whitespace()) {
|
||||||
None => return "",
|
None => return "",
|
||||||
|
|
||||||
Some((next_field_i, _)) => i = next_field_i,
|
Some((next_field_i, _)) => i = next_field_i,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,9 +192,9 @@ impl Uniq {
|
||||||
|| self.delimiters == Delimiters::Both)
|
|| self.delimiters == Delimiters::Both)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_line<W: Write>(
|
fn print_line(
|
||||||
&self,
|
&self,
|
||||||
writer: &mut BufWriter<W>,
|
writer: &mut impl Write,
|
||||||
line: &str,
|
line: &str,
|
||||||
count: usize,
|
count: usize,
|
||||||
first_line_printed: bool,
|
first_line_printed: bool,
|
||||||
|
@ -209,7 +206,7 @@ impl Uniq {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.show_counts {
|
if self.show_counts {
|
||||||
writer.write_all(format!("{count:7} {line}").as_bytes())
|
write!(writer, "{count:7} {line}")
|
||||||
} else {
|
} else {
|
||||||
writer.write_all(line.as_bytes())
|
writer.write_all(line.as_bytes())
|
||||||
}
|
}
|
||||||
|
@ -245,19 +242,12 @@ fn opt_parsed<T: FromStr>(opt_name: &str, matches: &ArgMatches) -> UResult<Optio
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
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 files: Vec<String> = matches
|
let files = matches.get_many::<OsString>(ARG_FILES);
|
||||||
.get_many::<String>(ARG_FILES)
|
|
||||||
.map(|v| v.map(ToString::to_string).collect())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let (in_file_name, out_file_name) = match files.len() {
|
let (in_file_name, out_file_name) = files
|
||||||
0 => ("-".to_owned(), "-".to_owned()),
|
.map(|fi| fi.map(AsRef::as_ref))
|
||||||
1 => (files[0].clone(), "-".to_owned()),
|
.map(|mut fi| (fi.next(), fi.next()))
|
||||||
2 => (files[0].clone(), files[1].clone()),
|
.unwrap_or_default();
|
||||||
_ => {
|
|
||||||
unreachable!() // Cannot happen as clap will fail earlier
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let uniq = Uniq {
|
let uniq = Uniq {
|
||||||
repeats_only: matches.get_flag(options::REPEATED)
|
repeats_only: matches.get_flag(options::REPEATED)
|
||||||
|
@ -282,8 +272,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
uniq.print_uniq(
|
uniq.print_uniq(
|
||||||
&mut open_input_file(&in_file_name)?,
|
open_input_file(in_file_name)?,
|
||||||
&mut open_output_file(&out_file_name)?,
|
open_output_file(out_file_name)?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,6 +377,7 @@ pub fn uu_app() -> Command {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(ARG_FILES)
|
Arg::new(ARG_FILES)
|
||||||
.action(ArgAction::Append)
|
.action(ArgAction::Append)
|
||||||
|
.value_parser(ValueParser::os_string())
|
||||||
.num_args(0..=2)
|
.num_args(0..=2)
|
||||||
.value_hint(clap::ValueHint::FilePath),
|
.value_hint(clap::ValueHint::FilePath),
|
||||||
)
|
)
|
||||||
|
@ -412,26 +403,26 @@ fn get_delimiter(matches: &ArgMatches) -> Delimiters {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_input_file(in_file_name: &str) -> UResult<BufReader<Box<dyn Read + 'static>>> {
|
// None or "-" means stdin.
|
||||||
let in_file = if in_file_name == "-" {
|
fn open_input_file(in_file_name: Option<&OsStr>) -> UResult<Box<dyn BufRead>> {
|
||||||
Box::new(stdin()) as Box<dyn Read>
|
Ok(match in_file_name {
|
||||||
} else {
|
Some(path) if path != "-" => {
|
||||||
let path = Path::new(in_file_name);
|
let in_file = File::open(path)
|
||||||
let in_file = File::open(path)
|
.map_err_context(|| format!("Could not open {}", path.maybe_quote()))?;
|
||||||
.map_err_context(|| format!("Could not open {}", in_file_name.maybe_quote()))?;
|
Box::new(BufReader::new(in_file))
|
||||||
Box::new(in_file) as Box<dyn Read>
|
}
|
||||||
};
|
_ => Box::new(stdin().lock()),
|
||||||
Ok(BufReader::new(in_file))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_output_file(out_file_name: &str) -> UResult<BufWriter<Box<dyn Write + 'static>>> {
|
// None or "-" means stdout.
|
||||||
let out_file = if out_file_name == "-" {
|
fn open_output_file(out_file_name: Option<&OsStr>) -> UResult<Box<dyn Write>> {
|
||||||
Box::new(stdout()) as Box<dyn Write>
|
Ok(match out_file_name {
|
||||||
} else {
|
Some(path) if path != "-" => {
|
||||||
let path = Path::new(out_file_name);
|
let out_file = File::create(path)
|
||||||
let out_file = File::create(path)
|
.map_err_context(|| format!("Could not open {}", path.maybe_quote()))?;
|
||||||
.map_err_context(|| format!("Could not create {}", out_file_name.maybe_quote()))?;
|
Box::new(BufWriter::new(out_file))
|
||||||
Box::new(out_file) as Box<dyn Write>
|
}
|
||||||
};
|
_ => Box::new(stdout().lock()),
|
||||||
Ok(BufWriter::new(out_file))
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue