mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
parent
95de5f6494
commit
e3cac647f6
2 changed files with 74 additions and 3 deletions
|
@ -7,13 +7,14 @@
|
||||||
// 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.
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) ctype cwidth iflag nbytes nspaces nums tspaces uflag
|
// spell-checker:ignore (ToDO) ctype cwidth iflag nbytes nspaces nums tspaces uflag Preprocess
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
use clap::{crate_version, Arg, ArgMatches, Command};
|
use clap::{crate_version, Arg, ArgMatches, Command};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::ffi::OsString;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
|
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
|
||||||
|
@ -21,7 +22,7 @@ use std::num::IntErrorKind;
|
||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
use unicode_width::UnicodeWidthChar;
|
use unicode_width::UnicodeWidthChar;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UError, UResult};
|
use uucore::error::{FromIo, UError, UResult, UUsageError};
|
||||||
use uucore::format_usage;
|
use uucore::format_usage;
|
||||||
|
|
||||||
static ABOUT: &str = "Convert tabs in each FILE to spaces, writing to standard output.
|
static ABOUT: &str = "Convert tabs in each FILE to spaces, writing to standard output.
|
||||||
|
@ -61,6 +62,11 @@ fn is_space_or_comma(c: char) -> bool {
|
||||||
c == ' ' || c == ','
|
c == ' ' || c == ','
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decide whether the character is either a digit or a comma.
|
||||||
|
fn is_digit_or_comma(c: char) -> bool {
|
||||||
|
c.is_ascii_digit() || c == ','
|
||||||
|
}
|
||||||
|
|
||||||
/// Errors that can occur when parsing a `--tabs` argument.
|
/// Errors that can occur when parsing a `--tabs` argument.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ParseError {
|
enum ParseError {
|
||||||
|
@ -243,9 +249,35 @@ impl Options {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Preprocess command line arguments and expand shortcuts. For example, "-7" is expanded to
|
||||||
|
/// "--tabs=7" and "-1,3" to "--tabs=1 --tabs=3".
|
||||||
|
fn expand_shortcuts<'a>(
|
||||||
|
mut args: impl uucore::Args + 'a,
|
||||||
|
) -> Result<Box<dyn Iterator<Item = OsString> + 'a>, Box<(dyn UError + 'static)>> {
|
||||||
|
// argv[0] is always present
|
||||||
|
let mut processed_args = vec![args.next().unwrap()];
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
if let Some(s) = arg.to_str() {
|
||||||
|
if s.starts_with('-') && s[1..].chars().all(is_digit_or_comma) {
|
||||||
|
s[1..]
|
||||||
|
.split(',')
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.for_each(|s| processed_args.push(format!("--tabs={}", s).into()));
|
||||||
|
} else {
|
||||||
|
processed_args.push(arg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(UUsageError::new(1, "bad argument encoding".to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Box::new(processed_args.into_iter()))
|
||||||
|
}
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let matches = uu_app().get_matches_from(args);
|
let matches = uu_app().get_matches_from(expand_shortcuts(args)?);
|
||||||
|
|
||||||
expand(&Options::new(&matches)?).map_err_context(|| "failed to write output".to_string())
|
expand(&Options::new(&matches)?).map_err_context(|| "failed to write output".to_string())
|
||||||
}
|
}
|
||||||
|
@ -445,6 +477,8 @@ fn expand(options: &Options) -> std::io::Result<()> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::is_digit_or_comma;
|
||||||
|
|
||||||
use super::next_tabstop;
|
use super::next_tabstop;
|
||||||
use super::RemainingMode;
|
use super::RemainingMode;
|
||||||
|
|
||||||
|
@ -468,4 +502,11 @@ mod tests {
|
||||||
assert_eq!(next_tabstop(&[1, 5], 3, &RemainingMode::Slash), 2);
|
assert_eq!(next_tabstop(&[1, 5], 3, &RemainingMode::Slash), 2);
|
||||||
assert_eq!(next_tabstop(&[1, 5], 6, &RemainingMode::Slash), 4);
|
assert_eq!(next_tabstop(&[1, 5], 6, &RemainingMode::Slash), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_digit_or_comma() {
|
||||||
|
assert!(is_digit_or_comma('1'));
|
||||||
|
assert!(is_digit_or_comma(','));
|
||||||
|
assert!(!is_digit_or_comma('a'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,3 +263,33 @@ fn test_tabs_with_too_large_size() {
|
||||||
|
|
||||||
new_ucmd!().arg(arg).fails().stderr_contains(expected_error);
|
new_ucmd!().arg(arg).fails().stderr_contains(expected_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tabs_shortcut() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-2", "-5", "-7"])
|
||||||
|
.pipe_in("\ta\tb\tc")
|
||||||
|
.succeeds()
|
||||||
|
// 01234567890
|
||||||
|
.stdout_is(" a b c");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_comma_separated_tabs_shortcut() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-2,5", "-7"])
|
||||||
|
.pipe_in("\ta\tb\tc")
|
||||||
|
.succeeds()
|
||||||
|
// 01234567890
|
||||||
|
.stdout_is(" a b c");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tabs_and_tabs_shortcut_mixed() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-2", "--tabs=5", "-7"])
|
||||||
|
.pipe_in("\ta\tb\tc")
|
||||||
|
.succeeds()
|
||||||
|
// 01234567890
|
||||||
|
.stdout_is(" a b c");
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue