From 45751e9e485633c21f1b5cc49deda7c85ec828a3 Mon Sep 17 00:00:00 2001 From: Eli Youngs Date: Tue, 1 Feb 2022 22:47:30 -0800 Subject: [PATCH 01/60] cp: Create backup before hardlink --- src/uu/cp/src/cp.rs | 9 +++++++++ tests/by-util/test_cp.rs | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index f8ce6f241..fa9a1df65 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1309,6 +1309,15 @@ fn copy_file( match options.copy_mode { CopyMode::Link => { + if dest.exists() { + let backup_path = + backup_control::get_backup_path(options.backup, &dest, &options.backup_suffix); + if let Some(backup_path) = backup_path { + backup_dest(&dest, &backup_path)?; + fs::remove_file(&dest)?; + } + } + fs::hard_link(&source, &dest).context(context)?; } CopyMode::Copy => { diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 92637dfbe..52081eb52 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1444,3 +1444,17 @@ fn test_cp_archive_on_nonexistent_file() { "cp: cannot stat 'nonexistent_file.txt': No such file or directory (os error 2)", ); } + +#[test] +fn test_cp_link_backup() { + let (at, mut ucmd) = at_and_ucmd!(); + at.touch("file2"); + ucmd.arg("-l") + .arg("-b") + .arg(TEST_HELLO_WORLD_SOURCE) + .arg("file2") + .succeeds(); + + assert!(at.file_exists("file2~")); + assert_eq!(at.read("file2"), "Hello, World!\n"); +} From 75b3cbcfd97ba45df9bd41d6da0d8c7ef34312eb Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 23 Feb 2022 21:56:24 +0100 Subject: [PATCH 02/60] pr: move from getopts to clap fixes --- Cargo.lock | 10 - src/uu/pr/Cargo.toml | 1 - src/uu/pr/src/pr.rs | 631 ++++++++++++++++++++----------------------- 3 files changed, 298 insertions(+), 344 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd064dc76..805c1c83a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -890,15 +890,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - [[package]] name = "getrandom" version = "0.2.4" @@ -2905,7 +2896,6 @@ version = "0.0.12" dependencies = [ "chrono", "clap 3.0.10", - "getopts", "itertools", "quick-error", "regex", diff --git a/src/uu/pr/Cargo.toml b/src/uu/pr/Cargo.toml index a1fdae4a2..8e9029daa 100644 --- a/src/uu/pr/Cargo.toml +++ b/src/uu/pr/Cargo.toml @@ -17,7 +17,6 @@ path = "src/pr.rs" [dependencies] clap = { version = "3.0", features = ["wrap_help", "cargo"] } uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["entries"] } -getopts = "0.2.21" chrono = "0.4.19" quick-error = "2.0.1" itertools = "0.10.0" diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index c167770c0..8ba3872f5 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -1,5 +1,3 @@ -#![crate_name = "uu_pr"] - // This file is part of the uutils coreutils package. // // For the full copyright and license information, please view the LICENSE file @@ -13,9 +11,7 @@ extern crate quick_error; use chrono::offset::Local; use chrono::DateTime; -use clap::{App, AppSettings}; -use getopts::Matches; -use getopts::{HasArg, Occur}; +use clap::{App, AppSettings, Arg, ArgMatches}; use itertools::Itertools; use quick_error::ResultExt; use regex::Regex; @@ -26,14 +22,35 @@ use std::io::{stdin, stdout, BufRead, BufReader, Lines, Read, Write}; use std::os::unix::fs::FileTypeExt; use uucore::display::Quotable; -use uucore::error::UResult; +use uucore::error::{set_exit_code, UResult}; -type IOError = std::io::Error; - -const NAME: &str = "pr"; const VERSION: &str = env!("CARGO_PKG_VERSION"); const ABOUT: &str = "Write content of given file or standard input to standard output with pagination filter"; +const AFTER_HELP: &str = + " +PAGE\n Begin output at page number page of the formatted input. + -COLUMN\n Produce multi-column output. See --column + +The pr utility is a printing and pagination filter +for text files. When multiple input files are specified, +each is read, formatted, and written to standard +output. By default, the input is separated +into 66-line pages, each with + +o A 5-line header with the page number, date, + time, and the pathname of the file. + +o A 5-line trailer consisting of blank lines. + +If standard output is associated with a terminal, +diagnostic messages are suppressed until the pr +utility has completed processing. + +When multiple column output is specified, text columns +are of equal width. By default text columns +are separated by at least one . Input lines +that do not fit into a text column are truncated. +Lines are not truncated under single column output."; const TAB: char = '\t'; const LINES_PER_PAGE: usize = 66; const LINES_PER_PAGE_FOR_FORM_FEED: usize = 63; @@ -47,25 +64,27 @@ const DEFAULT_COLUMN_SEPARATOR: &char = &TAB; const FF: u8 = 0x0C_u8; mod options { - pub const STRING_HEADER_OPTION: &str = "h"; - pub const DOUBLE_SPACE_OPTION: &str = "d"; - pub const NUMBERING_MODE_OPTION: &str = "n"; - pub const FIRST_LINE_NUMBER_OPTION: &str = "N"; - pub const PAGE_RANGE_OPTION: &str = "pages"; - pub const NO_HEADER_TRAILER_OPTION: &str = "t"; - pub const PAGE_LENGTH_OPTION: &str = "l"; - pub const SUPPRESS_PRINTING_ERROR: &str = "r"; - pub const FORM_FEED_OPTION: &str = "F"; - pub const FORM_FEED_OPTION_SMALL: &str = "f"; - pub const COLUMN_WIDTH_OPTION: &str = "w"; - pub const PAGE_WIDTH_OPTION: &str = "W"; - pub const ACROSS_OPTION: &str = "a"; - pub const COLUMN_OPTION: &str = "column"; - pub const COLUMN_CHAR_SEPARATOR_OPTION: &str = "s"; - pub const COLUMN_STRING_SEPARATOR_OPTION: &str = "S"; - pub const MERGE_FILES_PRINT: &str = "m"; - pub const OFFSET_SPACES_OPTION: &str = "o"; - pub const JOIN_LINES_OPTION: &str = "J"; + pub const HEADER: &str = "header"; + pub const DOUBLE_SPACE: &str = "double-space"; + pub const NUMBER_LINES: &str = "number-lines"; + pub const FIRST_LINE_NUMBER: &str = "first-line-number"; + pub const PAGES: &str = "pages"; + pub const OMIT_HEADER: &str = "omit-header"; + pub const PAGE_LENGTH: &str = "length"; + pub const NO_FILE_WARNINGS: &str = "no-file-warnings"; + pub const FORM_FEED: &str = "form-feed"; + pub const COLUMN_WIDTH: &str = "width"; + pub const PAGE_WIDTH: &str = "page-width"; + pub const ACROSS: &str = "across"; + pub const COLUMN: &str = "column"; + pub const COLUMN_CHAR_SEPARATOR: &str = "separator"; + pub const COLUMN_STRING_SEPARATOR: &str = "sep-string"; + pub const MERGE: &str = "merge"; + pub const INDENT: &str = "indent"; + pub const JOIN_LINES: &str = "join-lines"; + pub const HELP: &str = "help"; + pub const VERSION: &str = "version"; + pub const FILES: &str = "files"; } struct OutputOptions { @@ -95,7 +114,7 @@ struct FileLine { line_number: usize, page_number: usize, group_key: usize, - line_content: Result, + line_content: Result, form_feeds_after: usize, } @@ -136,8 +155,8 @@ impl Default for FileLine { } } -impl From for PrError { - fn from(err: IOError) -> Self { +impl From for PrError { + fn from(err: std::io::Error) -> Self { Self::EncounteredErrors(err.to_string()) } } @@ -145,8 +164,8 @@ impl From for PrError { quick_error! { #[derive(Debug)] enum PrError { - Input(err: IOError, path: String) { - context(path: &'a str, err: IOError) -> (err, path.to_owned()) + Input(err: std::io::Error, path: String) { + context(path: &'a str, err: std::io::Error) -> (err, path.to_owned()) display("pr: Reading from input {0} gave error", path) source(err) } @@ -177,7 +196,182 @@ pub fn uu_app<'a>() -> App<'a> { App::new(uucore::util_name()) .version(VERSION) .about(ABOUT) + .after_help(AFTER_HELP) .setting(AppSettings::InferLongArgs) + .setting(AppSettings::AllArgsOverrideSelf) + .setting(AppSettings::NoAutoHelp) + .setting(AppSettings::NoAutoVersion) + .arg( + Arg::new(options::PAGES) + .long(options::PAGES) + .help("Begin and stop printing with page FIRST_PAGE[:LAST_PAGE]") + .takes_value(true) + .value_name("FIRST_PAGE[:LAST_PAGE]"), + ) + .arg( + Arg::new(options::HEADER) + .short('h') + .long(options::HEADER) + .help( + "Use the string header to replace the file name \ + in the header line.", + ) + .takes_value(true) + .value_name("STRING"), + ) + .arg( + Arg::new(options::DOUBLE_SPACE) + .short('d') + .long(options::DOUBLE_SPACE) + .help("Produce output that is double spaced. An extra character is output following every + found in the input.") + ) + .arg( + Arg::new(options::NUMBER_LINES) + .short('n') + .long(options::NUMBER_LINES) + .help("Provide width digit line numbering. The default for width, if not specified, is 5. The number occupies + the first width column positions of each text column or each line of -m output. If char (any non-digit + character) is given, it is appended to the line number to separate it from whatever follows. The default + for char is a . Line numbers longer than width columns are truncated.") + .takes_value(true) + .value_name("[char][width]") + ) + .arg( + Arg::new(options::FIRST_LINE_NUMBER) + .short('N') + .long(options::FIRST_LINE_NUMBER) + .help("start counting with NUMBER at 1st line of first page printed") + .takes_value(true) + .value_name("NUMBER") + ) + .arg( + Arg::new(options::OMIT_HEADER) + .short('t') + .long(options::OMIT_HEADER) + .help("Write neither the five-line identifying header nor the five-line trailer usually supplied for each page. Quit + writing after the last line of each file without spacing to the end of the page.") + ) + .arg( + Arg::new(options::PAGE_LENGTH) + .short('l') + .long(options::PAGE_LENGTH) + .help("Override the 66-line default (default number of lines of text 56, and with -F 63) and reset the page length to lines. If lines is not greater than the sum of both + the header and trailer depths (in lines), the pr utility shall suppress both the header and trailer, as if the + -t option were in effect. ") + .takes_value(true) + .value_name("PAGE_LENGTH") + ) + .arg( + Arg::new(options::NO_FILE_WARNINGS) + .short('r') + .long(options::NO_FILE_WARNINGS) + .help("omit warning when a file cannot be opened") + ) + .arg( + Arg::new(options::FORM_FEED) + .short('F') + .short_alias('f') + .long(options::FORM_FEED) + .help("Use a for new pages, instead of the default behavior that uses a sequence of s.") + ) + .arg( + Arg::new(options::COLUMN_WIDTH) + .short('w') + .long(options::COLUMN_WIDTH) + .help("Set the width of the line to width column positions for multiple text-column output only. If the -w option is + not specified and the -s option is not specified, the default width shall be 72. If the -w option is not specified + and the -s option is specified, the default width shall be 512.") + .takes_value(true) + .value_name("width") + ) + .arg( + Arg::new(options::PAGE_WIDTH) + .short('W') + .long(options::PAGE_WIDTH) + .help("set page width to PAGE_WIDTH (72) characters always, + truncate lines, except -J option is set, no interference + with -S or -s") + .takes_value(true) + .value_name("width") + ) + .arg( + Arg::new(options::ACROSS) + .short('a') + .long(options::ACROSS) + .help("Modify the effect of the - column option so that the columns are filled across the page in a round-robin order + (for example, when column is 2, the first input line heads column 1, the second heads column 2, the third is the + second line in column 1, and so on).") + ) + .arg( + Arg::new(options::COLUMN) + .long(options::COLUMN) + .help("Produce multi-column output that is arranged in column columns (the default shall be 1) and is written down each + column in the order in which the text is received from the input file. This option should not be used with -m. + The options -e and -i shall be assumed for multiple text-column output. Whether or not text columns are produced + with identical vertical lengths is unspecified, but a text column shall never exceed the length of the + page (see the -l option). When used with -t, use the minimum number of lines to write the output.") + .takes_value(true) + .value_name("column") + ) + .arg( + Arg::new(options::COLUMN_CHAR_SEPARATOR) + .short('s') + .long(options::COLUMN_CHAR_SEPARATOR) + .help("Separate text columns by the single character char instead of by the appropriate number of s + (default for char is the character).") + .takes_value(true) + .value_name("char") + ) + .arg( + Arg::new(options::COLUMN_STRING_SEPARATOR) + .short('S') + .long(options::COLUMN_STRING_SEPARATOR) + .help("separate columns by STRING, + without -S: Default separator with -J and + otherwise (same as -S\" \"), no effect on column options") + .takes_value(true) + .value_name("string") + ) + .arg( + Arg::new(options::MERGE) + .short('m') + .long(options::MERGE) + .help("Merge files. Standard output shall be formatted so the pr utility writes one line from each file specified by a + file operand, side by side into text columns of equal fixed widths, in terms of the number of column positions. + Implementations shall support merging of at least nine file operands.") + ) + .arg( + Arg::new(options::INDENT) + .short('o') + .long(options::INDENT) + .help("Each line of output shall be preceded by offset s. If the -o option is not specified, the default offset + shall be zero. The space taken is in addition to the output line width (see the -w option below).") + .takes_value(true) + .value_name("margin") + ) + .arg( + Arg::new(options::JOIN_LINES) + .short('J') + .help("merge full lines, turns off -W line truncation, no column + alignment, --sep-string[=STRING] sets separators") + ) + .arg( + Arg::new(options::HELP) + .long(options::HELP) + .help("Show this help message") + ) + .arg( + Arg::new(options::VERSION) + .short('V') + .long(options::VERSION) + .help("Show version information") + ) + .arg( + Arg::new(options::FILES) + .multiple_occurrences(true) + .multiple_values(true) + ) } #[uucore::main] @@ -185,229 +379,39 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = args .collect_str(uucore::InvalidEncodingHandling::Ignore) .accept_any(); - let mut opts = getopts::Options::new(); - - opts.opt( - "", - options::PAGE_RANGE_OPTION, - "Begin and stop printing with page FIRST_PAGE[:LAST_PAGE]", - "FIRST_PAGE[:LAST_PAGE]", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::STRING_HEADER_OPTION, - "header", - "Use the string header to replace the file name \ - in the header line.", - "STRING", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::DOUBLE_SPACE_OPTION, - "double-space", - "Produce output that is double spaced. An extra character is output following every - found in the input.", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - options::NUMBERING_MODE_OPTION, - "number-lines", - "Provide width digit line numbering. The default for width, if not specified, is 5. The number occupies - the first width column positions of each text column or each line of -m output. If char (any non-digit - character) is given, it is appended to the line number to separate it from whatever follows. The default - for char is a . Line numbers longer than width columns are truncated.", - "[char][width]", - HasArg::Maybe, - Occur::Optional, - ); - - opts.opt( - options::FIRST_LINE_NUMBER_OPTION, - "first-line-number", - "start counting with NUMBER at 1st line of first page printed", - "NUMBER", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::NO_HEADER_TRAILER_OPTION, - "omit-header", - "Write neither the five-line identifying header nor the five-line trailer usually supplied for each page. Quit - writing after the last line of each file without spacing to the end of the page.", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - options::PAGE_LENGTH_OPTION, - "length", - "Override the 66-line default (default number of lines of text 56, and with -F 63) and reset the page length to lines. If lines is not greater than the sum of both - the header and trailer depths (in lines), the pr utility shall suppress both the header and trailer, as if the - -t option were in effect. ", - "lines", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::SUPPRESS_PRINTING_ERROR, - "no-file-warnings", - "omit warning when a file cannot be opened", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - options::FORM_FEED_OPTION, - "form-feed", - "Use a for new pages, instead of the default behavior that uses a sequence of s.", - "", - HasArg::No, - Occur::Optional, - ); - opts.opt( - options::FORM_FEED_OPTION_SMALL, - "form-feed", - "Same as -F but pause before beginning the first page if standard output is a - terminal.", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - "", - options::COLUMN_OPTION, - "Produce multi-column output that is arranged in column columns (the default shall be 1) and is written down each - column in the order in which the text is received from the input file. This option should not be used with -m. - The options -e and -i shall be assumed for multiple text-column output. Whether or not text columns are produced - with identical vertical lengths is unspecified, but a text column shall never exceed the length of the - page (see the -l option). When used with -t, use the minimum number of lines to write the output.", - "[column]", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::COLUMN_WIDTH_OPTION, - "width", - "Set the width of the line to width column positions for multiple text-column output only. If the -w option is - not specified and the -s option is not specified, the default width shall be 72. If the -w option is not specified - and the -s option is specified, the default width shall be 512.", - "[width]", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::PAGE_WIDTH_OPTION, - "page-width", - "set page width to PAGE_WIDTH (72) characters always, - truncate lines, except -J option is set, no interference - with -S or -s", - "[width]", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::ACROSS_OPTION, - "across", - "Modify the effect of the - column option so that the columns are filled across the page in a round-robin order - (for example, when column is 2, the first input line heads column 1, the second heads column 2, the third is the - second line in column 1, and so on).", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - options::COLUMN_CHAR_SEPARATOR_OPTION, - "separator", - "Separate text columns by the single character char instead of by the appropriate number of s - (default for char is the character).", - "char", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::COLUMN_STRING_SEPARATOR_OPTION, - "sep-string", - "separate columns by STRING, - without -S: Default separator with -J and - otherwise (same as -S\" \"), no effect on column options", - "string", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::MERGE_FILES_PRINT, - "merge", - "Merge files. Standard output shall be formatted so the pr utility writes one line from each file specified by a - file operand, side by side into text columns of equal fixed widths, in terms of the number of column positions. - Implementations shall support merging of at least nine file operands.", - "", - HasArg::No, - Occur::Optional, - ); - - opts.opt( - options::OFFSET_SPACES_OPTION, - "indent", - "Each line of output shall be preceded by offset s. If the -o option is not specified, the default offset - shall be zero. The space taken is in addition to the output line width (see the -w option below).", - "offset", - HasArg::Yes, - Occur::Optional, - ); - - opts.opt( - options::JOIN_LINES_OPTION, - "join-lines", - "merge full lines, turns off -W line truncation, no column - alignment, --sep-string[=STRING] sets separators", - "offset", - HasArg::No, - Occur::Optional, - ); - - opts.optflag("", "help", "display this help and exit"); - opts.optflag("V", "version", "output version information and exit"); let opt_args = recreate_arguments(&args); - let matches = match opts.parse(&opt_args[1..]) { + let mut app = uu_app(); + let matches = match app.try_get_matches_from_mut(opt_args) { Ok(m) => m, - Err(e) => panic!("Invalid options\n{}", e), + Err(e) => { + e.print()?; + set_exit_code(1); + return Ok(()); + } }; - if matches.opt_present("version") { - println!("{} {}", NAME, VERSION); + if matches.is_present(options::VERSION) { + println!("{}", app.render_long_version()); return Ok(()); } - let mut files = matches.free.clone(); + if matches.is_present(options::HELP) { + app.print_long_help()?; + return Ok(()); + } + + let mut files = matches + .values_of(options::FILES) + .map(|v| v.collect::>()) + .unwrap_or_default() + .clone(); if files.is_empty() { - files.insert(0, FILE_STDIN.to_owned()); + files.insert(0, FILE_STDIN); } - if matches.opt_present("help") { - return print_usage(&mut opts, &matches); - } - - let file_groups: Vec<_> = if matches.opt_present(options::MERGE_FILES_PRINT) { + let file_groups: Vec<_> = if matches.is_present(options::MERGE) { vec![files] } else { files.into_iter().map(|i| vec![i]).collect() @@ -470,56 +474,13 @@ fn recreate_arguments(args: &[String]) -> Vec { .collect() } -fn print_error(matches: &Matches, err: &PrError) { - if !matches.opt_present(options::SUPPRESS_PRINTING_ERROR) { +fn print_error(matches: &ArgMatches, err: &PrError) { + if !matches.is_present(options::NO_FILE_WARNINGS) { eprintln!("{}", err); } } -fn print_usage(opts: &mut getopts::Options, matches: &Matches) -> UResult<()> { - println!("{} {} -- print files", NAME, VERSION); - println!(); - println!( - "Usage: {} [+page] [-column] [-adFfmprt] [[-e] [char] [gap]] - [-L locale] [-h header] [[-i] [char] [gap]] - [-l lines] [-o offset] [[-s] [char]] [[-n] [char] - [width]] [-w width] [-] [file ...].", - NAME - ); - println!(); - let usage: &str = "The pr utility is a printing and pagination filter - for text files. When multiple input files are specified, - each is read, formatted, and written to standard - output. By default, the input is separated - into 66-line pages, each with - - o A 5-line header with the page number, date, - time, and the pathname of the file. - - o A 5-line trailer consisting of blank lines. - - If standard output is associated with a terminal, - diagnostic messages are suppressed until the pr - utility has completed processing. - - When multiple column output is specified, text columns - are of equal width. By default text columns - are separated by at least one . Input lines - that do not fit into a text column are truncated. - Lines are not truncated under single column output."; - println!("{}", opts.usage(usage)); - println!(" +page \t\tBegin output at page number page of the formatted input."); - println!( - " -column \t\tProduce multi-column output. Refer --{}", - options::COLUMN_OPTION - ); - if matches.free.is_empty() { - return Err(1.into()); - } - Ok(()) -} - -fn parse_usize(matches: &Matches, opt: &str) -> Option> { +fn parse_usize(matches: &ArgMatches, opt: &str) -> Option> { let from_parse_error_to_pr_error = |value_to_parse: (String, String)| { let i = value_to_parse.0; let option = value_to_parse.1; @@ -528,51 +489,51 @@ fn parse_usize(matches: &Matches, opt: &str) -> Option> { }) }; matches - .opt_str(opt) - .map(|i| (i, format!("-{}", opt))) + .value_of(opt) + .map(|i| (i.to_string(), format!("-{}", opt))) .map(from_parse_error_to_pr_error) } fn build_options( - matches: &Matches, - paths: &[String], + matches: &ArgMatches, + paths: &[&str], free_args: &str, ) -> Result { - let form_feed_used = matches.opt_present(options::FORM_FEED_OPTION) - || matches.opt_present(options::FORM_FEED_OPTION_SMALL); + let form_feed_used = matches.is_present(options::FORM_FEED); - let is_merge_mode = matches.opt_present(options::MERGE_FILES_PRINT); + let is_merge_mode = matches.is_present(options::MERGE); - if is_merge_mode && matches.opt_present(options::COLUMN_OPTION) { + if is_merge_mode && matches.is_present(options::COLUMN) { let err_msg = String::from("cannot specify number of columns when printing in parallel"); return Err(PrError::EncounteredErrors(err_msg)); } - if is_merge_mode && matches.opt_present(options::ACROSS_OPTION) { + if is_merge_mode && matches.is_present(options::ACROSS) { let err_msg = String::from("cannot specify both printing across and printing in parallel"); return Err(PrError::EncounteredErrors(err_msg)); } - let merge_files_print = if matches.opt_present(options::MERGE_FILES_PRINT) { + let merge_files_print = if matches.is_present(options::MERGE) { Some(paths.len()) } else { None }; - let header = matches.opt_str(options::STRING_HEADER_OPTION).unwrap_or( - if is_merge_mode || paths[0] == FILE_STDIN { - String::new() + let header = matches + .value_of(options::HEADER) + .unwrap_or(if is_merge_mode || paths[0] == FILE_STDIN { + "" } else { - paths[0].to_string() - }, - ); + paths[0] + }) + .to_string(); let default_first_number = NumberingMode::default().first_number; - let first_number = parse_usize(matches, options::FIRST_LINE_NUMBER_OPTION) - .unwrap_or(Ok(default_first_number))?; + let first_number = + parse_usize(matches, options::FIRST_LINE_NUMBER).unwrap_or(Ok(default_first_number))?; let number = matches - .opt_str(options::NUMBERING_MODE_OPTION) + .value_of(options::NUMBER_LINES) .map(|i| { let parse_result = i.parse::(); @@ -596,14 +557,14 @@ fn build_options( } }) .or_else(|| { - if matches.opt_present(options::NUMBERING_MODE_OPTION) { + if matches.is_present(options::NUMBER_LINES) { Some(NumberingMode::default()) } else { None } }); - let double_space = matches.opt_present(options::DOUBLE_SPACE_OPTION); + let double_space = matches.is_present(options::DOUBLE_SPACE); let content_line_separator = if double_space { "\n".repeat(2) @@ -652,7 +613,7 @@ fn build_options( }; let invalid_pages_map = |i: String| { - let unparsed_value = matches.opt_str(options::PAGE_RANGE_OPTION).unwrap(); + let unparsed_value = matches.value_of(options::PAGES).unwrap(); i.parse::().map_err(|_e| { PrError::EncounteredErrors(format!( "invalid --pages argument {}", @@ -662,7 +623,7 @@ fn build_options( }; let start_page = match matches - .opt_str(options::PAGE_RANGE_OPTION) + .value_of(options::PAGES) .map(|i| { let x: Vec<_> = i.split(':').collect(); x[0].to_string() @@ -674,7 +635,7 @@ fn build_options( }; let end_page = match matches - .opt_str(options::PAGE_RANGE_OPTION) + .value_of(options::PAGES) .filter(|i| i.contains(':')) .map(|i| { let x: Vec<_> = i.split(':').collect(); @@ -702,12 +663,12 @@ fn build_options( }; let page_length = - parse_usize(matches, options::PAGE_LENGTH_OPTION).unwrap_or(Ok(default_lines_per_page))?; + parse_usize(matches, options::PAGE_LENGTH).unwrap_or(Ok(default_lines_per_page))?; let page_length_le_ht = page_length < (HEADER_LINES_PER_PAGE + TRAILER_LINES_PER_PAGE); let display_header_and_trailer = - !(page_length_le_ht) && !matches.opt_present(options::NO_HEADER_TRAILER_OPTION); + !(page_length_le_ht) && !matches.is_present(options::OMIT_HEADER); let content_lines_per_page = if page_length_le_ht { page_length @@ -715,23 +676,24 @@ fn build_options( page_length - (HEADER_LINES_PER_PAGE + TRAILER_LINES_PER_PAGE) }; - let page_separator_char = if matches.opt_present(options::FORM_FEED_OPTION) { + let page_separator_char = if matches.is_present(options::FORM_FEED) { let bytes = vec![FF]; String::from_utf8(bytes).unwrap() } else { "\n".to_string() }; - let across_mode = matches.opt_present(options::ACROSS_OPTION); + let across_mode = matches.is_present(options::ACROSS); - let column_separator = match matches.opt_str(options::COLUMN_STRING_SEPARATOR_OPTION) { + let column_separator = match matches.value_of(options::COLUMN_STRING_SEPARATOR) { Some(x) => Some(x), - None => matches.opt_str(options::COLUMN_CHAR_SEPARATOR_OPTION), + None => matches.value_of(options::COLUMN_CHAR_SEPARATOR), } + .map(ToString::to_string) .unwrap_or_else(|| DEFAULT_COLUMN_SEPARATOR.to_string()); - let default_column_width = if matches.opt_present(options::COLUMN_WIDTH_OPTION) - && matches.opt_present(options::COLUMN_CHAR_SEPARATOR_OPTION) + let default_column_width = if matches.is_present(options::COLUMN_WIDTH) + && matches.is_present(options::COLUMN_CHAR_SEPARATOR) { DEFAULT_COLUMN_WIDTH_WITH_S_OPTION } else { @@ -739,12 +701,12 @@ fn build_options( }; let column_width = - parse_usize(matches, options::COLUMN_WIDTH_OPTION).unwrap_or(Ok(default_column_width))?; + parse_usize(matches, options::COLUMN_WIDTH).unwrap_or(Ok(default_column_width))?; - let page_width = if matches.opt_present(options::JOIN_LINES_OPTION) { + let page_width = if matches.is_present(options::JOIN_LINES) { None } else { - match parse_usize(matches, options::PAGE_WIDTH_OPTION) { + match parse_usize(matches, options::PAGE_WIDTH) { Some(res) => Some(res?), None => None, } @@ -764,7 +726,7 @@ fn build_options( // --column has more priority than -column - let column_option_value = match parse_usize(matches, options::COLUMN_OPTION) { + let column_option_value = match parse_usize(matches, options::COLUMN) { Some(res) => Some(res?), None => start_column_option, }; @@ -776,9 +738,8 @@ fn build_options( across_mode, }); - let offset_spaces = - " ".repeat(parse_usize(matches, options::OFFSET_SPACES_OPTION).unwrap_or(Ok(0))?); - let join_lines = matches.opt_present(options::JOIN_LINES_OPTION); + let offset_spaces = " ".repeat(parse_usize(matches, options::INDENT).unwrap_or(Ok(0))?); + let join_lines = matches.is_present(options::JOIN_LINES); let col_sep_for_printing = column_mode_options .as_ref() @@ -855,7 +816,7 @@ fn open(path: &str) -> Result, PrError> { .unwrap_or_else(|_| Err(PrError::NotExists(path.to_string()))) } -fn split_lines_if_form_feed(file_content: Result) -> Vec { +fn split_lines_if_form_feed(file_content: Result) -> Vec { file_content .map(|content| { let mut lines = Vec::new(); @@ -972,7 +933,7 @@ fn read_stream_and_create_pages( ) } -fn mpr(paths: &[String], options: &OutputOptions) -> Result { +fn mpr(paths: &[&str], options: &OutputOptions) -> Result { let n_files = paths.len(); // Check if files exists @@ -1032,7 +993,11 @@ fn mpr(paths: &[String], options: &OutputOptions) -> Result { Ok(0) } -fn print_page(lines: &[FileLine], options: &OutputOptions, page: usize) -> Result { +fn print_page( + lines: &[FileLine], + options: &OutputOptions, + page: usize, +) -> Result { let line_separator = options.line_separator.as_bytes(); let page_separator = options.page_separator_char.as_bytes(); @@ -1064,7 +1029,7 @@ fn write_columns( lines: &[FileLine], options: &OutputOptions, out: &mut impl Write, -) -> Result { +) -> Result { let line_separator = options.content_line_separator.as_bytes(); let content_lines_per_page = if options.double_space { From bb379b53841f652fc582aa1b3c9a56e9c04024e4 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Wed, 23 Feb 2022 23:12:05 +0100 Subject: [PATCH 03/60] pr: fix heuristic for number-lines argument (#3109) --- src/uu/pr/src/pr.rs | 3 ++- tests/by-util/test_pr.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index 8ba3872f5..79baf72c9 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -235,6 +235,7 @@ pub fn uu_app<'a>() -> App<'a> { character) is given, it is appended to the line number to separate it from whatever follows. The default for char is a . Line numbers longer than width columns are truncated.") .takes_value(true) + .allow_hyphen_values(true) .value_name("[char][width]") ) .arg( @@ -453,7 +454,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { /// * `args` - Command line arguments fn recreate_arguments(args: &[String]) -> Vec { let column_page_option = Regex::new(r"^[-+]\d+.*").unwrap(); - let num_regex = Regex::new(r"(.\d+)|(\d+)|^[^-]$").unwrap(); + let num_regex = Regex::new(r"^[^-]\d*$").unwrap(); //let a_file: Regex = Regex::new(r"^[^-+].*").unwrap(); let n_regex = Regex::new(r"^-n\s*$").unwrap(); let mut arguments = args.to_owned(); diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index 5c16e7acc..2e72aaed7 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -448,3 +448,16 @@ fn test_with_join_lines_option() { &valid_last_modified_template_vars(start), ); } + +#[test] +fn test_value_for_number_lines() { + // *5 is of the form [SEP[NUMBER]] so is accepted and succeeds + new_ucmd!().args(&["-n", "*5", "test.log"]).succeeds(); + + // a is of the form [SEP[NUMBER]] so is accepted and succeeds + new_ucmd!().args(&["-n", "a", "test.log"]).succeeds(); + + // foo5.txt is of not the form [SEP[NUMBER]] so is not used as value. + // Therefore, pr tries to access the file, which does not exist. + new_ucmd!().args(&["-n", "foo5.txt", "test.log"]).fails(); +} From ee36dea1a93a67a9b93af510e0d2d75dd5f84aa1 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Wed, 5 Jan 2022 21:06:04 -0500 Subject: [PATCH 04/60] split: implement outputting kth chunk of file Implement `-n l/k/N` option, where the `k`th chunk of the input file is written to stdout. For example, $ seq -w 0 99 > f; split -n l/3/10 f 20 21 22 23 24 25 26 27 28 29 --- src/uu/split/src/split.rs | 72 ++++++++++++++++ tests/by-util/test_split.rs | 8 ++ tests/fixtures/split/onehundredlines.txt | 100 +++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 tests/fixtures/split/onehundredlines.txt diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index e2504f305..090d89d4e 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -859,6 +859,11 @@ where /// /// This function returns an error if there is a problem reading from /// `reader` or writing to one of the output files. +/// +/// # See also +/// +/// * [`kth_chunk_by_line`], which splits its input in the same way, +/// but writes only one specified chunk to stdout. fn split_into_n_chunks_by_line( settings: &Settings, reader: &mut R, @@ -915,6 +920,67 @@ where Ok(()) } +/// Print the k-th chunk of a file, splitting by line. +/// +/// This function is like [`split_into_n_chunks_by_line`], but instead +/// of writing each chunk to its own file, it only writes to stdout +/// the contents of the chunk identified by `chunk_number`. +/// +/// # Errors +/// +/// This function returns an error if there is a problem reading from +/// `reader` or writing to one of the output files. +/// +/// # See also +/// +/// * [`split_into_n_chunks_by_line`], which splits its input in the +/// same way, but writes each chunk to its own file. +fn kth_chunk_by_line( + settings: &Settings, + reader: &mut R, + chunk_number: u64, + num_chunks: u64, +) -> UResult<()> +where + R: BufRead, +{ + // Get the size of the input file in bytes and compute the number + // of bytes per chunk. + let metadata = metadata(&settings.input).unwrap(); + let num_bytes = metadata.len(); + let chunk_size = (num_bytes / (num_chunks as u64)) as usize; + + // Write to stdout instead of to a file. + let stdout = std::io::stdout(); + let mut writer = stdout.lock(); + + let mut num_bytes_remaining_in_current_chunk = chunk_size; + let mut i = 0; + for line_result in reader.lines() { + let line = line_result?; + let bytes = line.as_bytes(); + if i == chunk_number { + writer.write_all(bytes)?; + writer.write_all(b"\n")?; + } + + // Add one byte for the newline character. + let num_bytes = bytes.len() + 1; + if num_bytes >= num_bytes_remaining_in_current_chunk { + num_bytes_remaining_in_current_chunk = chunk_size; + i += 1; + } else { + num_bytes_remaining_in_current_chunk -= num_bytes; + } + + if i > chunk_number { + break; + } + } + + Ok(()) +} + fn split(settings: &Settings) -> UResult<()> { let mut reader = BufReader::new(if settings.input == "-" { Box::new(stdin()) as Box @@ -935,6 +1001,12 @@ fn split(settings: &Settings) -> UResult<()> { Strategy::Number(NumberType::Lines(num_chunks)) => { split_into_n_chunks_by_line(settings, &mut reader, num_chunks) } + Strategy::Number(NumberType::KthLines(chunk_number, num_chunks)) => { + // The chunk number is given as a 1-indexed number, but it + // is a little easier to deal with a 0-indexed number. + let chunk_number = chunk_number - 1; + kth_chunk_by_line(settings, &mut reader, chunk_number, num_chunks) + } Strategy::Number(_) => Err(USimpleError::new(1, "-n mode not yet fully implemented")), Strategy::Lines(chunk_size) => { let mut writer = LineChunkWriter::new(chunk_size, settings) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index ab59a573a..06aa9ea61 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -587,3 +587,11 @@ fn test_lines() { assert_eq!(file_read("xaa"), "1\n2\n3\n"); assert_eq!(file_read("xab"), "4\n5\n"); } + +#[test] +fn test_lines_kth() { + new_ucmd!() + .args(&["-n", "l/3/10", "onehundredlines.txt"]) + .succeeds() + .stdout_only("20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n"); +} diff --git a/tests/fixtures/split/onehundredlines.txt b/tests/fixtures/split/onehundredlines.txt new file mode 100644 index 000000000..f2abdb403 --- /dev/null +++ b/tests/fixtures/split/onehundredlines.txt @@ -0,0 +1,100 @@ +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 From f3bd1f302047502cafc24e4b467fcc92488ed65a Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 4 Mar 2022 22:22:19 +0100 Subject: [PATCH 05/60] Add onehundredlines in the spell ignore --- tests/by-util/test_split.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 06aa9ea61..08431a8f6 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -2,7 +2,7 @@ // * // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -// spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes threebytes asciilowercase fghij klmno pqrst uvwxyz fivelines twohundredfortyonebytes +// spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes threebytes asciilowercase fghij klmno pqrst uvwxyz fivelines twohundredfortyonebytes onehundredlines extern crate rand; extern crate regex; From 3fb36d02e3c1816265b6e6be82586c40937efe84 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 19 Feb 2022 19:14:21 -0500 Subject: [PATCH 06/60] df: add unit tests for internal helper functions Add unit tests in the `df.rs` file for internal helper functions `mount_info_lt()`, `is_best()`, `is_included()`, and `filter_mount_list()`. --- src/uu/df/src/df.rs | 321 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 320 insertions(+), 1 deletion(-) diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index b7d27e245..6d480662e 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -5,7 +5,7 @@ // // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. -// spell-checker:ignore itotal iused iavail ipcent pcent +// spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs mod table; #[cfg(unix)] @@ -431,3 +431,322 @@ pub fn uu_app<'a>() -> App<'a> { ) .arg(Arg::new(OPT_PATHS).multiple_occurrences(true)) } + +#[cfg(test)] +mod tests { + + mod mount_info_lt { + + use crate::mount_info_lt; + use uucore::fsext::MountInfo; + + /// Instantiate a [`MountInfo`] with the given fields. + fn mount_info(dev_name: &str, mount_root: &str, mount_dir: &str) -> MountInfo { + MountInfo { + dev_id: String::new(), + dev_name: String::from(dev_name), + fs_type: String::new(), + mount_dir: String::from(mount_dir), + mount_option: String::new(), + mount_root: String::from(mount_root), + remote: false, + dummy: false, + } + } + + #[test] + fn test_absolute() { + // Prefer device name "/dev/foo" over "dev_foo". + let m1 = mount_info("/dev/foo", "/", "/mnt/bar"); + let m2 = mount_info("dev_foo", "/", "/mnt/bar"); + assert!(!mount_info_lt(&m1, &m2)); + } + + #[test] + fn test_shorter() { + // Prefer mount directory "/mnt/bar" over "/mnt/bar/baz"... + let m1 = mount_info("/dev/foo", "/", "/mnt/bar"); + let m2 = mount_info("/dev/foo", "/", "/mnt/bar/baz"); + assert!(!mount_info_lt(&m1, &m2)); + + // ..but prefer mount root "/root" over "/". + let m1 = mount_info("/dev/foo", "/root", "/mnt/bar"); + let m2 = mount_info("/dev/foo", "/", "/mnt/bar/baz"); + assert!(mount_info_lt(&m1, &m2)); + } + + #[test] + fn test_over_mounted() { + // Prefer the earlier entry if the devices are different but + // the mount directory is the same. + let m1 = mount_info("/dev/foo", "/", "/mnt/baz"); + let m2 = mount_info("/dev/bar", "/", "/mnt/baz"); + assert!(!mount_info_lt(&m1, &m2)); + } + } + + mod is_best { + + use crate::is_best; + use uucore::fsext::MountInfo; + + /// Instantiate a [`MountInfo`] with the given fields. + fn mount_info(dev_id: &str, mount_dir: &str) -> MountInfo { + MountInfo { + dev_id: String::from(dev_id), + dev_name: String::new(), + fs_type: String::new(), + mount_dir: String::from(mount_dir), + mount_option: String::new(), + mount_root: String::new(), + remote: false, + dummy: false, + } + } + + #[test] + fn test_empty() { + let m = mount_info("0", "/mnt/bar"); + assert!(is_best(&[], &m)); + } + + #[test] + fn test_different_dev_id() { + let m1 = mount_info("0", "/mnt/bar"); + let m2 = mount_info("1", "/mnt/bar"); + assert!(is_best(&[m1.clone()], &m2)); + assert!(is_best(&[m2], &m1)); + } + + #[test] + fn test_same_dev_id() { + // There are several conditions under which a `MountInfo` is + // considered "better" than the others, we're just checking + // one condition in this test. + let m1 = mount_info("0", "/mnt/bar"); + let m2 = mount_info("0", "/mnt/bar/baz"); + assert!(!is_best(&[m1.clone()], &m2)); + assert!(is_best(&[m2], &m1)); + } + } + + mod is_included { + + use crate::{is_included, FsSelector, Options}; + use std::collections::HashSet; + use uucore::fsext::MountInfo; + + /// Instantiate a [`MountInfo`] with the given fields. + fn mount_info(fs_type: &str, mount_dir: &str, remote: bool, dummy: bool) -> MountInfo { + MountInfo { + dev_id: String::new(), + dev_name: String::new(), + fs_type: String::from(fs_type), + mount_dir: String::from(mount_dir), + mount_option: String::new(), + mount_root: String::new(), + remote, + dummy, + } + } + + #[test] + fn test_remote_included() { + let opt = Default::default(); + let paths = []; + let m = mount_info("ext4", "/mnt/foo", true, false); + assert!(is_included(&m, &paths, &opt)); + } + + #[test] + fn test_remote_excluded() { + let opt = Options { + show_local_fs: true, + ..Default::default() + }; + let paths = []; + let m = mount_info("ext4", "/mnt/foo", true, false); + assert!(!is_included(&m, &paths, &opt)); + } + + #[test] + fn test_dummy_included() { + let opt = Options { + show_all_fs: true, + show_listed_fs: true, + ..Default::default() + }; + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, true); + assert!(is_included(&m, &paths, &opt)); + } + + #[test] + fn test_dummy_excluded() { + let opt = Default::default(); + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, true); + assert!(!is_included(&m, &paths, &opt)); + } + + #[test] + fn test_exclude_match() { + let exclude = HashSet::from([String::from("ext4")]); + let fs_selector = FsSelector { + exclude, + ..Default::default() + }; + let opt = Options { + fs_selector, + ..Default::default() + }; + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, false); + assert!(!is_included(&m, &paths, &opt)); + } + + #[test] + fn test_exclude_no_match() { + let exclude = HashSet::from([String::from("tmpfs")]); + let fs_selector = FsSelector { + exclude, + ..Default::default() + }; + let opt = Options { + fs_selector, + ..Default::default() + }; + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, false); + assert!(is_included(&m, &paths, &opt)); + } + + #[test] + fn test_include_match() { + let include = HashSet::from([String::from("ext4")]); + let fs_selector = FsSelector { + include, + ..Default::default() + }; + let opt = Options { + fs_selector, + ..Default::default() + }; + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, false); + assert!(is_included(&m, &paths, &opt)); + } + + #[test] + fn test_include_no_match() { + let include = HashSet::from([String::from("tmpfs")]); + let fs_selector = FsSelector { + include, + ..Default::default() + }; + let opt = Options { + fs_selector, + ..Default::default() + }; + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, false); + assert!(!is_included(&m, &paths, &opt)); + } + + #[test] + fn test_include_and_exclude_match_neither() { + let include = HashSet::from([String::from("tmpfs")]); + let exclude = HashSet::from([String::from("squashfs")]); + let fs_selector = FsSelector { include, exclude }; + let opt = Options { + fs_selector, + ..Default::default() + }; + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, false); + assert!(!is_included(&m, &paths, &opt)); + } + + #[test] + fn test_include_and_exclude_match_exclude() { + let include = HashSet::from([String::from("tmpfs")]); + let exclude = HashSet::from([String::from("ext4")]); + let fs_selector = FsSelector { include, exclude }; + let opt = Options { + fs_selector, + ..Default::default() + }; + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, false); + assert!(!is_included(&m, &paths, &opt)); + } + + #[test] + fn test_include_and_exclude_match_include() { + let include = HashSet::from([String::from("ext4")]); + let exclude = HashSet::from([String::from("squashfs")]); + let fs_selector = FsSelector { include, exclude }; + let opt = Options { + fs_selector, + ..Default::default() + }; + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, false); + assert!(is_included(&m, &paths, &opt)); + } + + #[test] + fn test_include_and_exclude_match_both() { + // TODO The same filesystem type in both `include` and + // `exclude` should cause an error, but currently does + // not. + let include = HashSet::from([String::from("ext4")]); + let exclude = HashSet::from([String::from("ext4")]); + let fs_selector = FsSelector { include, exclude }; + let opt = Options { + fs_selector, + ..Default::default() + }; + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, false); + assert!(!is_included(&m, &paths, &opt)); + } + + #[test] + fn test_paths_empty() { + let opt = Default::default(); + let paths = []; + let m = mount_info("ext4", "/mnt/foo", false, false); + assert!(is_included(&m, &paths, &opt)); + } + + #[test] + fn test_not_in_paths() { + let opt = Default::default(); + let paths = [String::from("/mnt/foo")]; + let m = mount_info("ext4", "/mnt/bar", false, false); + assert!(!is_included(&m, &paths, &opt)); + } + + #[test] + fn test_in_paths() { + let opt = Default::default(); + let paths = [String::from("/mnt/foo")]; + let m = mount_info("ext4", "/mnt/foo", false, false); + assert!(is_included(&m, &paths, &opt)); + } + } + + mod filter_mount_list { + + use crate::filter_mount_list; + + #[test] + fn test_empty() { + let opt = Default::default(); + let paths = []; + let mount_infos = vec![]; + assert!(filter_mount_list(mount_infos, &paths, &opt).is_empty()); + } + } +} From 41acdb5471784940fbeb359e3b32d573b5562429 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sun, 27 Feb 2022 11:18:45 -0500 Subject: [PATCH 07/60] df: borrow Row in DisplayRow::new() Change the signature of `DisplayRow::new()` to borrow `Row` instead of consuming it, so that the `Row` can be used after it is displayed. --- src/uu/df/src/df.rs | 2 +- src/uu/df/src/table.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 6d480662e..436c19247 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -309,7 +309,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .collect(); println!("{}", Header::new(&opt)); for row in data { - println!("{}", DisplayRow::new(row, &opt)); + println!("{}", DisplayRow::new(&row, &opt)); } Ok(()) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 2f7fba456..682d7836b 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -120,7 +120,7 @@ impl From for Row { /// The `options` control how the information in the row gets displayed. pub(crate) struct DisplayRow<'a> { /// The data in this row. - row: Row, + row: &'a Row, /// Options that control how to display the data. options: &'a Options, @@ -135,7 +135,7 @@ pub(crate) struct DisplayRow<'a> { impl<'a> DisplayRow<'a> { /// Instantiate this struct. - pub(crate) fn new(row: Row, options: &'a Options) -> Self { + pub(crate) fn new(row: &'a Row, options: &'a Options) -> Self { Self { row, options } } @@ -354,7 +354,7 @@ mod tests { inodes_usage: Some(0.2), }; assert_eq!( - DisplayRow::new(row, &options).to_string(), + DisplayRow::new(&row, &options).to_string(), "my_device 100 25 75 25% my_mount " ); } @@ -385,7 +385,7 @@ mod tests { inodes_usage: Some(0.2), }; assert_eq!( - DisplayRow::new(row, &options).to_string(), + DisplayRow::new(&row, &options).to_string(), "my_device my_type 100 25 75 25% my_mount " ); } @@ -416,7 +416,7 @@ mod tests { inodes_usage: Some(0.2), }; assert_eq!( - DisplayRow::new(row, &options).to_string(), + DisplayRow::new(&row, &options).to_string(), "my_device 10 2 8 20% my_mount " ); } @@ -447,7 +447,7 @@ mod tests { inodes_usage: Some(0.2), }; assert_eq!( - DisplayRow::new(row, &options).to_string(), + DisplayRow::new(&row, &options).to_string(), "my_device my_type 4.0k 1.0k 3.0k 25% my_mount " ); } @@ -478,7 +478,7 @@ mod tests { inodes_usage: Some(0.2), }; assert_eq!( - DisplayRow::new(row, &options).to_string(), + DisplayRow::new(&row, &options).to_string(), "my_device my_type 4.0Ki 1.0Ki 3.0Ki 25% my_mount " ); } From d0ebd1c9d06dd67e47207fc43fb571b818cf1ccb Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sun, 27 Feb 2022 11:20:27 -0500 Subject: [PATCH 08/60] df: add support for --total option Add support for the `--total` option to `df`, which displays the total of each numeric column. For example, $ df --total Filesystem 1K-blocks Used Available Use% Mounted on udev 3858016 0 3858016 0% /dev ... /dev/loop14 63488 63488 0 100% /snap/core20/1361 total 258775268 98099712 148220200 40% - --- src/uu/df/src/df.rs | 15 +++++++++++ src/uu/df/src/table.rs | 58 ++++++++++++++++++++++++++++++++++++++++ tests/by-util/test_df.rs | 44 ++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 436c19247..6371bb0be 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -106,6 +106,11 @@ impl From<&ArgMatches> for BlockSize { } } +/// Parameters that control the behavior of `df`. +/// +/// Most of these parameters control which rows and which columns are +/// displayed. The `block_size` determines the units to use when +/// displaying numbers of bytes or inodes. #[derive(Default)] struct Options { show_local_fs: bool, @@ -115,6 +120,9 @@ struct Options { show_inode_instead: bool, block_size: BlockSize, fs_selector: FsSelector, + + /// Whether to show a final row comprising the totals for each column. + show_total: bool, } impl Options { @@ -128,6 +136,7 @@ impl Options { show_inode_instead: matches.is_present(OPT_INODES), block_size: BlockSize::from(matches), fs_selector: FsSelector::from(matches), + show_total: matches.is_present(OPT_TOTAL), } } } @@ -307,9 +316,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs) .map(Into::into) .collect(); + println!("{}", Header::new(&opt)); + let mut total = Row::new("total"); for row in data { println!("{}", DisplayRow::new(&row, &opt)); + total += row; + } + if opt.show_total { + println!("{}", DisplayRow::new(&total, &opt)); } Ok(()) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 682d7836b..1b1c61d1f 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -15,6 +15,7 @@ use crate::{BlockSize, Filesystem, Options}; use uucore::fsext::{FsUsage, MountInfo}; use std::fmt; +use std::ops::AddAssign; /// A row in the filesystem usage data table. /// @@ -67,6 +68,63 @@ pub(crate) struct Row { inodes_usage: Option, } +impl Row { + pub(crate) fn new(source: &str) -> Self { + Self { + fs_device: source.into(), + fs_type: "-".into(), + fs_mount: "-".into(), + bytes: 0, + bytes_used: 0, + bytes_free: 0, + bytes_usage: None, + #[cfg(target_os = "macos")] + bytes_capacity: None, + inodes: 0, + inodes_used: 0, + inodes_free: 0, + inodes_usage: None, + } + } +} + +impl AddAssign for Row { + /// Sum the numeric values of two rows. + /// + /// The `Row::fs_device` field is set to `"total"` and the + /// remaining `String` fields are set to `"-"`. + fn add_assign(&mut self, rhs: Self) { + let bytes = self.bytes + rhs.bytes; + let bytes_used = self.bytes_used + rhs.bytes_used; + let inodes = self.inodes + rhs.inodes; + let inodes_used = self.inodes_used + rhs.inodes_used; + *self = Self { + fs_device: "total".into(), + fs_type: "-".into(), + fs_mount: "-".into(), + bytes, + bytes_used, + bytes_free: self.bytes_free + rhs.bytes_free, + bytes_usage: if bytes == 0 { + None + } else { + Some(bytes_used as f64 / bytes as f64) + }, + // TODO Figure out how to compute this. + #[cfg(target_os = "macos")] + bytes_capacity: None, + inodes, + inodes_used, + inodes_free: self.inodes_free + rhs.inodes_free, + inodes_usage: if inodes == 0 { + None + } else { + Some(inodes_used as f64 / inodes as f64) + }, + } + } +} + impl From for Row { fn from(fs: Filesystem) -> Self { let MountInfo { diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 3af02428e..513423766 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -1,3 +1,4 @@ +// spell-checker:ignore udev use crate::common::util::*; #[test] @@ -77,4 +78,47 @@ fn test_type_option() { new_ucmd!().args(&["-t", "ext4", "-t", "ext3"]).succeeds(); } +#[test] +fn test_total() { + // Example output: + // + // Filesystem 1K-blocks Used Available Use% Mounted on + // udev 3858016 0 3858016 0% /dev + // ... + // /dev/loop14 63488 63488 0 100% /snap/core20/1361 + // total 258775268 98099712 148220200 40% - + let output = new_ucmd!().arg("--total").succeeds().stdout_move_str(); + + // Skip the header line. + let lines: Vec<&str> = output.lines().skip(1).collect(); + + // Parse the values from the last row. + let last_line = lines.last().unwrap(); + let mut iter = last_line.split_whitespace(); + assert_eq!(iter.next().unwrap(), "total"); + let reported_total_size = iter.next().unwrap().parse().unwrap(); + let reported_total_used = iter.next().unwrap().parse().unwrap(); + let reported_total_avail = iter.next().unwrap().parse().unwrap(); + + // Loop over each row except the last, computing the sum of each column. + let mut computed_total_size = 0; + let mut computed_total_used = 0; + let mut computed_total_avail = 0; + let n = lines.len(); + for line in &lines[..n - 1] { + let mut iter = line.split_whitespace(); + iter.next().unwrap(); + computed_total_size += iter.next().unwrap().parse::().unwrap(); + computed_total_used += iter.next().unwrap().parse::().unwrap(); + computed_total_avail += iter.next().unwrap().parse::().unwrap(); + } + + // Check that the sum of each column matches the reported value in + // the last row. + assert_eq!(computed_total_size, reported_total_size); + assert_eq!(computed_total_used, reported_total_used); + assert_eq!(computed_total_avail, reported_total_avail); + // TODO We could also check here that the use percentage matches. +} + // ToDO: more tests... From 6174cad3347bd1793b00bb1845e771ad2048400d Mon Sep 17 00:00:00 2001 From: xxyzz Date: Fri, 4 Mar 2022 17:21:38 +0800 Subject: [PATCH 09/60] Run GNU coverage job on pull requests GNU coverage job now takes around one hour to finish thanks to contributors' work. Run it on pull requests to compare the GNU coverage report. --- .github/workflows/GnuTests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index 9582b2af7..3b332a51c 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -201,7 +201,6 @@ jobs: gnu_coverage: name: Run GNU tests with coverage runs-on: ubuntu-latest - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} steps: - name: Checkout code uutil uses: actions/checkout@v2 From 6951e18c417478658a0f5f0deb4b2cec64924523 Mon Sep 17 00:00:00 2001 From: 353fc443 <353fc443@pm.me> Date: Wed, 2 Mar 2022 19:13:53 +0000 Subject: [PATCH 10/60] timeout: avoid panicking for empty string --- src/uu/timeout/src/timeout.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/uu/timeout/src/timeout.rs b/src/uu/timeout/src/timeout.rs index 2caed8cab..2493442b2 100644 --- a/src/uu/timeout/src/timeout.rs +++ b/src/uu/timeout/src/timeout.rs @@ -51,7 +51,7 @@ struct Config { } impl Config { - fn from(options: &clap::ArgMatches) -> Self { + fn from(options: &clap::ArgMatches) -> UResult { let signal = match options.value_of(options::SIGNAL) { Some(signal_) => { let signal_result = signal_by_name_or_value(signal_); @@ -69,8 +69,11 @@ impl Config { .value_of(options::KILL_AFTER) .map(|time| uucore::parse_time::from_str(time).unwrap()); - let duration: Duration = - uucore::parse_time::from_str(options.value_of(options::DURATION).unwrap()).unwrap(); + let duration = + match uucore::parse_time::from_str(options.value_of(options::DURATION).unwrap()) { + Ok(duration) => duration, + Err(err) => return Err(USimpleError::new(1, err)), + }; let preserve_status: bool = options.is_present(options::PRESERVE_STATUS); let foreground = options.is_present(options::FOREGROUND); @@ -82,7 +85,7 @@ impl Config { .map(String::from) .collect::>(); - Self { + Ok(Self { foreground, kill_after, signal, @@ -90,7 +93,7 @@ impl Config { preserve_status, verbose, command, - } + }) } } @@ -104,7 +107,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = app.get_matches_from(args); - let config = Config::from(&matches); + let config = Config::from(&matches)?; timeout( &config.command, config.duration, From 14e3f50651deeedc1e2754645e4cfb7fa8465e72 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Wed, 23 Feb 2022 21:24:41 -0500 Subject: [PATCH 11/60] df: move BlockSize to a new module, blocks.rs --- src/uu/df/src/blocks.rs | 56 +++++++++++++++++++++++++++++++++++++++++ src/uu/df/src/df.rs | 51 ++----------------------------------- 2 files changed, 58 insertions(+), 49 deletions(-) create mode 100644 src/uu/df/src/blocks.rs diff --git a/src/uu/df/src/blocks.rs b/src/uu/df/src/blocks.rs new file mode 100644 index 000000000..bb6a86df5 --- /dev/null +++ b/src/uu/df/src/blocks.rs @@ -0,0 +1,56 @@ +// * This file is part of the uutils coreutils package. +// * +// * For the full copyright and license information, please view the LICENSE +// * file that was distributed with this source code. +//! Types for representing and displaying block sizes. +use crate::{OPT_HUMAN_READABLE, OPT_HUMAN_READABLE_2}; +use clap::ArgMatches; + +/// A block size to use in condensing the display of a large number of bytes. +/// +/// The [`BlockSize::Bytes`] variant represents a static block +/// size. The [`BlockSize::HumanReadableDecimal`] and +/// [`BlockSize::HumanReadableBinary`] variants represent dynamic +/// block sizes: as the number of bytes increases, the divisor +/// increases as well (for example, from 1 to 1,000 to 1,000,000 and +/// so on in the case of [`BlockSize::HumanReadableDecimal`]). +/// +/// The default variant is `Bytes(1024)`. +pub(crate) enum BlockSize { + /// A fixed number of bytes. + /// + /// The number must be positive. + Bytes(u64), + + /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc. + /// + /// This variant represents powers of 1,000. Contrast with + /// [`BlockSize::HumanReadableBinary`], which represents powers of + /// 1,024. + HumanReadableDecimal, + + /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc. + /// + /// This variant represents powers of 1,024. Contrast with + /// [`BlockSize::HumanReadableDecimal`], which represents powers + /// of 1,000. + HumanReadableBinary, +} + +impl Default for BlockSize { + fn default() -> Self { + Self::Bytes(1024) + } +} + +impl From<&ArgMatches> for BlockSize { + fn from(matches: &ArgMatches) -> Self { + if matches.is_present(OPT_HUMAN_READABLE) { + Self::HumanReadableBinary + } else if matches.is_present(OPT_HUMAN_READABLE_2) { + Self::HumanReadableDecimal + } else { + Self::default() + } + } +} diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 6371bb0be..5e3812375 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -6,6 +6,7 @@ // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. // spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs +mod blocks; mod table; #[cfg(unix)] @@ -21,6 +22,7 @@ use std::iter::FromIterator; #[cfg(windows)] use std::path::Path; +use crate::blocks::BlockSize; use crate::table::{DisplayRow, Header, Row}; static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\ @@ -57,55 +59,6 @@ struct FsSelector { exclude: HashSet, } -/// A block size to use in condensing the display of a large number of bytes. -/// -/// The [`BlockSize::Bytes`] variant represents a static block -/// size. The [`BlockSize::HumanReadableDecimal`] and -/// [`BlockSize::HumanReadableBinary`] variants represent dynamic -/// block sizes: as the number of bytes increases, the divisor -/// increases as well (for example, from 1 to 1,000 to 1,000,000 and -/// so on in the case of [`BlockSize::HumanReadableDecimal`]). -/// -/// The default variant is `Bytes(1024)`. -enum BlockSize { - /// A fixed number of bytes. - /// - /// The number must be positive. - Bytes(u64), - - /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc. - /// - /// This variant represents powers of 1,000. Contrast with - /// [`BlockSize::HumanReadableBinary`], which represents powers of - /// 1,024. - HumanReadableDecimal, - - /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc. - /// - /// This variant represents powers of 1,024. Contrast with - /// [`BlockSize::HumanReadableDecimal`], which represents powers - /// of 1,000. - HumanReadableBinary, -} - -impl Default for BlockSize { - fn default() -> Self { - Self::Bytes(1024) - } -} - -impl From<&ArgMatches> for BlockSize { - fn from(matches: &ArgMatches) -> Self { - if matches.is_present(OPT_HUMAN_READABLE) { - Self::HumanReadableBinary - } else if matches.is_present(OPT_HUMAN_READABLE_2) { - Self::HumanReadableDecimal - } else { - Self::default() - } - } -} - /// Parameters that control the behavior of `df`. /// /// Most of these parameters control which rows and which columns are From 5cf7139467b02f9474532d9a266840e15e5df460 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Wed, 23 Feb 2022 21:28:07 -0500 Subject: [PATCH 12/60] df: fix block size header for multiples of 1024 Correct the column header printed by `df` when the `--block-size` argument has a value that is a multiple of 1024. After this commit, the header looks like "1K" or "4M" or "117G", etc., depending on the particular value of the block size. For example: $ df --block-size=1024 | head -n1 Filesystem 1K-blocks Used Available Use% Mounted on $ df --block-size=2048 | head -n1 Filesystem 2K-blocks Used Available Use% Mounted on $ df --block-size=3072 | head -n1 Filesystem 3K-blocks Used Available Use% Mounted on $ df --block-size=4096 | head -n1 Filesystem 4K-blocks Used Available Use% Mounted on --- src/uu/df/src/blocks.rs | 159 ++++++++++++++++++++++++++++++++++++--- src/uu/df/src/df.rs | 33 ++++++-- src/uu/df/src/table.rs | 29 ++++--- tests/by-util/test_df.rs | 29 +++++++ 4 files changed, 225 insertions(+), 25 deletions(-) diff --git a/src/uu/df/src/blocks.rs b/src/uu/df/src/blocks.rs index bb6a86df5..10ec22012 100644 --- a/src/uu/df/src/blocks.rs +++ b/src/uu/df/src/blocks.rs @@ -3,8 +3,72 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. //! Types for representing and displaying block sizes. -use crate::{OPT_HUMAN_READABLE, OPT_HUMAN_READABLE_2}; +use crate::{OPT_BLOCKSIZE, OPT_HUMAN_READABLE, OPT_HUMAN_READABLE_2}; use clap::ArgMatches; +use std::fmt; +use std::num::ParseIntError; + +/// The first ten powers of 1024. +const IEC_BASES: [u128; 10] = [ + 1, + 1_024, + 1_048_576, + 1_073_741_824, + 1_099_511_627_776, + 1_125_899_906_842_624, + 1_152_921_504_606_846_976, + 1_180_591_620_717_411_303_424, + 1_208_925_819_614_629_174_706_176, + 1_237_940_039_285_380_274_899_124_224, +]; + +/// Suffixes for the first nine multi-byte unit suffixes. +const SUFFIXES: [char; 9] = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + +/// Convert a multiple of 1024 into a string like "12K" or "34M". +/// +/// # Examples +/// +/// Powers of 1024 become "1K", "1M", "1G", etc. +/// +/// ```rust,ignore +/// assert_eq!(to_magnitude_and_suffix_1024(1024).unwrap(), "1K"); +/// assert_eq!(to_magnitude_and_suffix_1024(1024 * 1024).unwrap(), "1M"); +/// assert_eq!(to_magnitude_and_suffix_1024(1024 * 1024 * 1024).unwrap(), "1G"); +/// ``` +/// +/// Multiples of those powers affect the magnitude part of the +/// returned string: +/// +/// ```rust,ignore +/// assert_eq!(to_magnitude_and_suffix_1024(123 * 1024).unwrap(), "123K"); +/// assert_eq!(to_magnitude_and_suffix_1024(456 * 1024 * 1024).unwrap(), "456M"); +/// assert_eq!(to_magnitude_and_suffix_1024(789 * 1024 * 1024 * 1024).unwrap(), "789G"); +/// ``` +fn to_magnitude_and_suffix_1024(n: u128) -> Result { + // Find the smallest power of 1024 that is larger than `n`. That + // number indicates which units and suffix to use. + for i in 0..IEC_BASES.len() - 1 { + if n < IEC_BASES[i + 1] { + return Ok(format!("{}{}", n / IEC_BASES[i], SUFFIXES[i])); + } + } + Err(()) +} + +/// Convert a number into a magnitude and a multi-byte unit suffix. +/// +/// # Errors +/// +/// If the number is too large to represent. +fn to_magnitude_and_suffix(n: u128) -> Result { + if n % 1024 == 0 { + to_magnitude_and_suffix_1024(n) + } else { + // TODO Implement this, probably using code from `numfmt`. + Ok("1kB".into()) + } +} /// A block size to use in condensing the display of a large number of bytes. /// @@ -43,14 +107,91 @@ impl Default for BlockSize { } } -impl From<&ArgMatches> for BlockSize { - fn from(matches: &ArgMatches) -> Self { - if matches.is_present(OPT_HUMAN_READABLE) { - Self::HumanReadableBinary - } else if matches.is_present(OPT_HUMAN_READABLE_2) { - Self::HumanReadableDecimal - } else { - Self::default() +pub(crate) fn block_size_from_matches(matches: &ArgMatches) -> Result { + if matches.is_present(OPT_HUMAN_READABLE) { + Ok(BlockSize::HumanReadableBinary) + } else if matches.is_present(OPT_HUMAN_READABLE_2) { + Ok(BlockSize::HumanReadableDecimal) + } else if matches.is_present(OPT_BLOCKSIZE) { + let s = matches.value_of(OPT_BLOCKSIZE).unwrap(); + Ok(BlockSize::Bytes(s.parse()?)) + } else { + Ok(Default::default()) + } +} + +impl fmt::Display for BlockSize { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::HumanReadableBinary => write!(f, "Size"), + Self::HumanReadableDecimal => write!(f, "Size"), + Self::Bytes(n) => match to_magnitude_and_suffix(*n as u128) { + Ok(s) => write!(f, "{}-blocks", s), + Err(_) => Err(fmt::Error), + }, } } } + +#[cfg(test)] +mod tests { + + use crate::blocks::{to_magnitude_and_suffix, BlockSize}; + + #[test] + fn test_to_magnitude_and_suffix_powers_of_1024() { + assert_eq!(to_magnitude_and_suffix(1024).unwrap(), "1K"); + assert_eq!(to_magnitude_and_suffix(2048).unwrap(), "2K"); + assert_eq!(to_magnitude_and_suffix(4096).unwrap(), "4K"); + assert_eq!(to_magnitude_and_suffix(1024 * 1024).unwrap(), "1M"); + assert_eq!(to_magnitude_and_suffix(2 * 1024 * 1024).unwrap(), "2M"); + assert_eq!(to_magnitude_and_suffix(1024 * 1024 * 1024).unwrap(), "1G"); + assert_eq!( + to_magnitude_and_suffix(34 * 1024 * 1024 * 1024).unwrap(), + "34G" + ); + } + + // TODO We have not yet implemented this behavior, but when we do, + // uncomment this test. + + // #[test] + // fn test_to_magnitude_and_suffix_not_powers_of_1024() { + // assert_eq!(to_magnitude_and_suffix(1).unwrap(), "1B"); + // assert_eq!(to_magnitude_and_suffix(999).unwrap(), "999B"); + + // assert_eq!(to_magnitude_and_suffix(1000).unwrap(), "1kB"); + // assert_eq!(to_magnitude_and_suffix(1001).unwrap(), "1.1kB"); + // assert_eq!(to_magnitude_and_suffix(1023).unwrap(), "1.1kB"); + // assert_eq!(to_magnitude_and_suffix(1025).unwrap(), "1.1kB"); + // assert_eq!(to_magnitude_and_suffix(999_000).unwrap(), "999kB"); + + // assert_eq!(to_magnitude_and_suffix(999_001).unwrap(), "1MB"); + // assert_eq!(to_magnitude_and_suffix(999_999).unwrap(), "1MB"); + // assert_eq!(to_magnitude_and_suffix(1_000_000).unwrap(), "1MB"); + // assert_eq!(to_magnitude_and_suffix(1_000_001).unwrap(), "1.1MB"); + // assert_eq!(to_magnitude_and_suffix(1_100_000).unwrap(), "1.1MB"); + // assert_eq!(to_magnitude_and_suffix(1_100_001).unwrap(), "1.2MB"); + // assert_eq!(to_magnitude_and_suffix(1_900_000).unwrap(), "1.9MB"); + // assert_eq!(to_magnitude_and_suffix(1_900_001).unwrap(), "2MB"); + // assert_eq!(to_magnitude_and_suffix(9_900_000).unwrap(), "9.9MB"); + // assert_eq!(to_magnitude_and_suffix(9_900_001).unwrap(), "10MB"); + // assert_eq!(to_magnitude_and_suffix(999_000_000).unwrap(), "999MB"); + + // assert_eq!(to_magnitude_and_suffix(999_000_001).unwrap(), "1GB"); + // assert_eq!(to_magnitude_and_suffix(1_000_000_000).unwrap(), "1GB"); + // // etc. + // } + + #[test] + fn test_block_size_display() { + assert_eq!(format!("{}", BlockSize::HumanReadableBinary), "Size"); + assert_eq!(format!("{}", BlockSize::HumanReadableDecimal), "Size"); + assert_eq!(format!("{}", BlockSize::Bytes(1024)), "1K-blocks"); + assert_eq!(format!("{}", BlockSize::Bytes(2 * 1024)), "2K-blocks"); + assert_eq!( + format!("{}", BlockSize::Bytes(3 * 1024 * 1024)), + "3M-blocks" + ); + } +} diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 5e3812375..65091b4cc 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -9,20 +9,22 @@ mod blocks; mod table; +use uucore::error::{UResult, USimpleError}; +use uucore::format_usage; #[cfg(unix)] use uucore::fsext::statfs; use uucore::fsext::{read_fs_list, FsUsage, MountInfo}; -use uucore::{error::UResult, format_usage}; use clap::{crate_version, App, AppSettings, Arg, ArgMatches}; use std::collections::HashSet; +use std::fmt; use std::iter::FromIterator; #[cfg(windows)] use std::path::Path; -use crate::blocks::BlockSize; +use crate::blocks::{block_size_from_matches, BlockSize}; use crate::table::{DisplayRow, Header, Row}; static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\ @@ -78,19 +80,36 @@ struct Options { show_total: bool, } +enum OptionsError { + InvalidBlockSize, +} + +impl fmt::Display for OptionsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + // TODO This should include the raw string provided as the argument. + // + // TODO This needs to vary based on whether `--block-size` + // or `-B` were provided. + Self::InvalidBlockSize => write!(f, "invalid --block-size argument"), + } + } +} + impl Options { /// Convert command-line arguments into [`Options`]. - fn from(matches: &ArgMatches) -> Self { - Self { + fn from(matches: &ArgMatches) -> Result { + Ok(Self { show_local_fs: matches.is_present(OPT_LOCAL), show_all_fs: matches.is_present(OPT_ALL), show_listed_fs: false, show_fs_type: matches.is_present(OPT_PRINT_TYPE), show_inode_instead: matches.is_present(OPT_INODES), - block_size: BlockSize::from(matches), + block_size: block_size_from_matches(matches) + .map_err(|_| OptionsError::InvalidBlockSize)?, fs_selector: FsSelector::from(matches), show_total: matches.is_present(OPT_TOTAL), - } + }) } } @@ -260,7 +279,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } } - let opt = Options::from(&matches); + let opt = Options::from(&matches).map_err(|e| USimpleError::new(1, format!("{}", e)))?; let mounts = read_fs_list(); let data: Vec = filter_mount_list(mounts, &paths, &opt) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 1b1c61d1f..6d4a3bdd1 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -306,13 +306,12 @@ impl fmt::Display for Header<'_> { write!(f, "{0: >12} ", "IFree")?; write!(f, "{0: >5} ", "IUse%")?; } else { - // TODO Support arbitrary positive scaling factors (from - // the `--block-size` command-line argument). - if let BlockSize::Bytes(_) = self.options.block_size { - write!(f, "{0: >12} ", "1k-blocks")?; - } else { - write!(f, "{0: >12} ", "Size")?; - }; + // `Display` is implemented for `BlockSize`, but `Display` + // only works when formatting an object into an empty + // format, `{}`. So we use `format!()` first to create the + // string, then use `write!()` to align the string and pad + // with spaces. + write!(f, "{0: >12} ", format!("{}", self.options.block_size))?; write!(f, "{0: >12} ", "Used")?; write!(f, "{0: >12} ", "Available")?; #[cfg(target_os = "macos")] @@ -335,7 +334,7 @@ mod tests { let options = Default::default(); assert_eq!( Header::new(&options).to_string(), - "Filesystem 1k-blocks Used Available Use% Mounted on " + "Filesystem 1K-blocks Used Available Use% Mounted on " ); } @@ -347,7 +346,7 @@ mod tests { }; assert_eq!( Header::new(&options).to_string(), - "Filesystem Type 1k-blocks Used Available Use% Mounted on " + "Filesystem Type 1K-blocks Used Available Use% Mounted on " ); } @@ -363,6 +362,18 @@ mod tests { ); } + #[test] + fn test_header_display_block_size_1024() { + let options = Options { + block_size: BlockSize::Bytes(3 * 1024), + ..Default::default() + }; + assert_eq!( + Header::new(&options).to_string(), + "Filesystem 3K-blocks Used Available Use% Mounted on " + ); + } + #[test] fn test_header_display_human_readable_binary() { let options = Options { diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 513423766..455c75c7c 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -28,6 +28,8 @@ fn test_df_compatible_si() { #[test] fn test_df_output() { + // TODO These should fail because `-total` should have two dashes, + // not just one. But they don't fail. if cfg!(target_os = "macos") { new_ucmd!().arg("-H").arg("-total").succeeds(). stdout_only("Filesystem Size Used Available Capacity Use% Mounted on \n"); @@ -121,4 +123,31 @@ fn test_total() { // TODO We could also check here that the use percentage matches. } +#[test] +fn test_block_size_1024() { + fn get_header(block_size: u64) -> String { + // TODO When #3057 is resolved, we should just use + // + // new_ucmd!().arg("--output=size").succeeds().stdout_move_str(); + // + // instead of parsing the entire `df` table as a string. + let output = new_ucmd!() + .args(&["-B", &format!("{}", block_size)]) + .succeeds() + .stdout_move_str(); + let first_line = output.lines().next().unwrap(); + let mut column_labels = first_line.split_whitespace(); + let size_column_label = column_labels.nth(1).unwrap(); + size_column_label.into() + } + + assert_eq!(get_header(1024), "1K-blocks"); + assert_eq!(get_header(2048), "2K-blocks"); + assert_eq!(get_header(4096), "4K-blocks"); + assert_eq!(get_header(1024 * 1024), "1M-blocks"); + assert_eq!(get_header(2 * 1024 * 1024), "2M-blocks"); + assert_eq!(get_header(1024 * 1024 * 1024), "1G-blocks"); + assert_eq!(get_header(34 * 1024 * 1024 * 1024), "34G-blocks"); +} + // ToDO: more tests... From 5683d0fba044c16dd735f0e1381dfb5e5f084fbf Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 5 Mar 2022 20:03:25 +0000 Subject: [PATCH 13/60] chore(deps): add renovate.json --- renovate.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..9dc8c6919 --- /dev/null +++ b/renovate.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "config:base" + ] +} From 19af43222b5ec8ad1cc0dc201aba1837bbfc5bea Mon Sep 17 00:00:00 2001 From: Davide Cavalca Date: Sat, 12 Feb 2022 21:36:53 -0800 Subject: [PATCH 14/60] Include license text in all published crates --- src/uu/arch/LICENSE | 1 + src/uu/base32/LICENSE | 1 + src/uu/base64/LICENSE | 1 + src/uu/basename/LICENSE | 1 + src/uu/basenc/LICENSE | 1 + src/uu/cat/LICENSE | 1 + src/uu/chcon/LICENSE | 1 + src/uu/chgrp/LICENSE | 1 + src/uu/chmod/LICENSE | 1 + src/uu/chown/LICENSE | 1 + src/uu/chroot/LICENSE | 1 + src/uu/cksum/LICENSE | 1 + src/uu/comm/LICENSE | 1 + src/uu/cp/LICENSE | 1 + src/uu/csplit/LICENSE | 1 + src/uu/cut/LICENSE | 1 + src/uu/date/LICENSE | 1 + src/uu/dd/LICENSE | 1 + src/uu/df/LICENSE | 1 + src/uu/dircolors/LICENSE | 1 + src/uu/dirname/LICENSE | 1 + src/uu/du/LICENSE | 1 + src/uu/echo/LICENSE | 1 + src/uu/env/LICENSE | 1 + src/uu/expand/LICENSE | 1 + src/uu/expr/LICENSE | 1 + src/uu/factor/LICENSE | 1 + src/uu/false/LICENSE | 1 + src/uu/fmt/LICENSE | 1 + src/uu/fold/LICENSE | 1 + src/uu/groups/LICENSE | 1 + src/uu/hashsum/LICENSE | 1 + src/uu/head/LICENSE | 1 + src/uu/hostid/LICENSE | 1 + src/uu/hostname/LICENSE | 1 + src/uu/id/LICENSE | 1 + src/uu/install/LICENSE | 1 + src/uu/join/LICENSE | 1 + src/uu/kill/LICENSE | 1 + src/uu/link/LICENSE | 1 + src/uu/ln/LICENSE | 1 + src/uu/logname/LICENSE | 1 + src/uu/ls/LICENSE | 1 + src/uu/mkdir/LICENSE | 1 + src/uu/mkfifo/LICENSE | 1 + src/uu/mknod/LICENSE | 1 + src/uu/mktemp/LICENSE | 1 + src/uu/more/LICENSE | 1 + src/uu/mv/LICENSE | 1 + src/uu/nice/LICENSE | 1 + src/uu/nl/LICENSE | 1 + src/uu/nohup/LICENSE | 1 + src/uu/nproc/LICENSE | 1 + src/uu/numfmt/LICENSE | 1 + src/uu/od/LICENSE | 1 + src/uu/paste/LICENSE | 1 + src/uu/pathchk/LICENSE | 1 + src/uu/pinky/LICENSE | 1 + src/uu/pr/LICENSE | 1 + src/uu/printenv/LICENSE | 1 + src/uu/printf/LICENSE | 1 + src/uu/ptx/LICENSE | 1 + src/uu/pwd/LICENSE | 1 + src/uu/readlink/LICENSE | 1 + src/uu/realpath/LICENSE | 1 + src/uu/relpath/LICENSE | 1 + src/uu/rm/LICENSE | 1 + src/uu/rmdir/LICENSE | 1 + src/uu/runcon/LICENSE | 1 + src/uu/seq/LICENSE | 1 + src/uu/shred/LICENSE | 1 + src/uu/shuf/LICENSE | 1 + src/uu/sleep/LICENSE | 1 + src/uu/sort/LICENSE | 1 + src/uu/split/LICENSE | 1 + src/uu/stat/LICENSE | 1 + src/uu/stdbuf/LICENSE | 1 + src/uu/sum/LICENSE | 1 + src/uu/sync/LICENSE | 1 + src/uu/tac/LICENSE | 1 + src/uu/tail/LICENSE | 1 + src/uu/tee/LICENSE | 1 + src/uu/test/LICENSE | 1 + src/uu/timeout/LICENSE | 1 + src/uu/touch/LICENSE | 1 + src/uu/tr/LICENSE | 1 + src/uu/true/LICENSE | 1 + src/uu/truncate/LICENSE | 1 + src/uu/tsort/LICENSE | 1 + src/uu/tty/LICENSE | 1 + src/uu/uname/LICENSE | 1 + src/uu/unexpand/LICENSE | 1 + src/uu/uniq/LICENSE | 1 + src/uu/unlink/LICENSE | 1 + src/uu/uptime/LICENSE | 1 + src/uu/users/LICENSE | 1 + src/uu/wc/LICENSE | 1 + src/uu/who/LICENSE | 1 + src/uu/whoami/LICENSE | 1 + src/uu/yes/LICENSE | 1 + src/uucore/LICENSE | 1 + src/uucore_procs/LICENSE | 1 + 102 files changed, 102 insertions(+) create mode 120000 src/uu/arch/LICENSE create mode 120000 src/uu/base32/LICENSE create mode 120000 src/uu/base64/LICENSE create mode 120000 src/uu/basename/LICENSE create mode 120000 src/uu/basenc/LICENSE create mode 120000 src/uu/cat/LICENSE create mode 120000 src/uu/chcon/LICENSE create mode 120000 src/uu/chgrp/LICENSE create mode 120000 src/uu/chmod/LICENSE create mode 120000 src/uu/chown/LICENSE create mode 120000 src/uu/chroot/LICENSE create mode 120000 src/uu/cksum/LICENSE create mode 120000 src/uu/comm/LICENSE create mode 120000 src/uu/cp/LICENSE create mode 120000 src/uu/csplit/LICENSE create mode 120000 src/uu/cut/LICENSE create mode 120000 src/uu/date/LICENSE create mode 120000 src/uu/dd/LICENSE create mode 120000 src/uu/df/LICENSE create mode 120000 src/uu/dircolors/LICENSE create mode 120000 src/uu/dirname/LICENSE create mode 120000 src/uu/du/LICENSE create mode 120000 src/uu/echo/LICENSE create mode 120000 src/uu/env/LICENSE create mode 120000 src/uu/expand/LICENSE create mode 120000 src/uu/expr/LICENSE create mode 120000 src/uu/factor/LICENSE create mode 120000 src/uu/false/LICENSE create mode 120000 src/uu/fmt/LICENSE create mode 120000 src/uu/fold/LICENSE create mode 120000 src/uu/groups/LICENSE create mode 120000 src/uu/hashsum/LICENSE create mode 120000 src/uu/head/LICENSE create mode 120000 src/uu/hostid/LICENSE create mode 120000 src/uu/hostname/LICENSE create mode 120000 src/uu/id/LICENSE create mode 120000 src/uu/install/LICENSE create mode 120000 src/uu/join/LICENSE create mode 120000 src/uu/kill/LICENSE create mode 120000 src/uu/link/LICENSE create mode 120000 src/uu/ln/LICENSE create mode 120000 src/uu/logname/LICENSE create mode 120000 src/uu/ls/LICENSE create mode 120000 src/uu/mkdir/LICENSE create mode 120000 src/uu/mkfifo/LICENSE create mode 120000 src/uu/mknod/LICENSE create mode 120000 src/uu/mktemp/LICENSE create mode 120000 src/uu/more/LICENSE create mode 120000 src/uu/mv/LICENSE create mode 120000 src/uu/nice/LICENSE create mode 120000 src/uu/nl/LICENSE create mode 120000 src/uu/nohup/LICENSE create mode 120000 src/uu/nproc/LICENSE create mode 120000 src/uu/numfmt/LICENSE create mode 120000 src/uu/od/LICENSE create mode 120000 src/uu/paste/LICENSE create mode 120000 src/uu/pathchk/LICENSE create mode 120000 src/uu/pinky/LICENSE create mode 120000 src/uu/pr/LICENSE create mode 120000 src/uu/printenv/LICENSE create mode 120000 src/uu/printf/LICENSE create mode 120000 src/uu/ptx/LICENSE create mode 120000 src/uu/pwd/LICENSE create mode 120000 src/uu/readlink/LICENSE create mode 120000 src/uu/realpath/LICENSE create mode 120000 src/uu/relpath/LICENSE create mode 120000 src/uu/rm/LICENSE create mode 120000 src/uu/rmdir/LICENSE create mode 120000 src/uu/runcon/LICENSE create mode 120000 src/uu/seq/LICENSE create mode 120000 src/uu/shred/LICENSE create mode 120000 src/uu/shuf/LICENSE create mode 120000 src/uu/sleep/LICENSE create mode 120000 src/uu/sort/LICENSE create mode 120000 src/uu/split/LICENSE create mode 120000 src/uu/stat/LICENSE create mode 120000 src/uu/stdbuf/LICENSE create mode 120000 src/uu/sum/LICENSE create mode 120000 src/uu/sync/LICENSE create mode 120000 src/uu/tac/LICENSE create mode 120000 src/uu/tail/LICENSE create mode 120000 src/uu/tee/LICENSE create mode 120000 src/uu/test/LICENSE create mode 120000 src/uu/timeout/LICENSE create mode 120000 src/uu/touch/LICENSE create mode 120000 src/uu/tr/LICENSE create mode 120000 src/uu/true/LICENSE create mode 120000 src/uu/truncate/LICENSE create mode 120000 src/uu/tsort/LICENSE create mode 120000 src/uu/tty/LICENSE create mode 120000 src/uu/uname/LICENSE create mode 120000 src/uu/unexpand/LICENSE create mode 120000 src/uu/uniq/LICENSE create mode 120000 src/uu/unlink/LICENSE create mode 120000 src/uu/uptime/LICENSE create mode 120000 src/uu/users/LICENSE create mode 120000 src/uu/wc/LICENSE create mode 120000 src/uu/who/LICENSE create mode 120000 src/uu/whoami/LICENSE create mode 120000 src/uu/yes/LICENSE create mode 120000 src/uucore/LICENSE create mode 120000 src/uucore_procs/LICENSE diff --git a/src/uu/arch/LICENSE b/src/uu/arch/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/arch/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/base32/LICENSE b/src/uu/base32/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/base32/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/base64/LICENSE b/src/uu/base64/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/base64/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/basename/LICENSE b/src/uu/basename/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/basename/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/basenc/LICENSE b/src/uu/basenc/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/basenc/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/cat/LICENSE b/src/uu/cat/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/cat/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/chcon/LICENSE b/src/uu/chcon/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/chcon/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/chgrp/LICENSE b/src/uu/chgrp/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/chgrp/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/chmod/LICENSE b/src/uu/chmod/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/chmod/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/chown/LICENSE b/src/uu/chown/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/chown/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/chroot/LICENSE b/src/uu/chroot/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/chroot/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/cksum/LICENSE b/src/uu/cksum/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/cksum/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/comm/LICENSE b/src/uu/comm/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/comm/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/cp/LICENSE b/src/uu/cp/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/cp/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/csplit/LICENSE b/src/uu/csplit/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/csplit/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/cut/LICENSE b/src/uu/cut/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/cut/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/date/LICENSE b/src/uu/date/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/date/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/dd/LICENSE b/src/uu/dd/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/dd/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/df/LICENSE b/src/uu/df/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/df/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/dircolors/LICENSE b/src/uu/dircolors/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/dircolors/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/dirname/LICENSE b/src/uu/dirname/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/dirname/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/du/LICENSE b/src/uu/du/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/du/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/echo/LICENSE b/src/uu/echo/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/echo/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/env/LICENSE b/src/uu/env/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/env/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/expand/LICENSE b/src/uu/expand/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/expand/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/expr/LICENSE b/src/uu/expr/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/expr/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/factor/LICENSE b/src/uu/factor/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/factor/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/false/LICENSE b/src/uu/false/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/false/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/fmt/LICENSE b/src/uu/fmt/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/fmt/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/fold/LICENSE b/src/uu/fold/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/fold/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/groups/LICENSE b/src/uu/groups/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/groups/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/hashsum/LICENSE b/src/uu/hashsum/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/hashsum/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/head/LICENSE b/src/uu/head/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/head/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/hostid/LICENSE b/src/uu/hostid/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/hostid/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/hostname/LICENSE b/src/uu/hostname/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/hostname/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/id/LICENSE b/src/uu/id/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/id/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/install/LICENSE b/src/uu/install/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/install/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/join/LICENSE b/src/uu/join/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/join/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/kill/LICENSE b/src/uu/kill/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/kill/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/link/LICENSE b/src/uu/link/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/link/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/ln/LICENSE b/src/uu/ln/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/ln/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/logname/LICENSE b/src/uu/logname/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/logname/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/ls/LICENSE b/src/uu/ls/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/ls/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/mkdir/LICENSE b/src/uu/mkdir/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/mkdir/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/mkfifo/LICENSE b/src/uu/mkfifo/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/mkfifo/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/mknod/LICENSE b/src/uu/mknod/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/mknod/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/mktemp/LICENSE b/src/uu/mktemp/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/mktemp/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/more/LICENSE b/src/uu/more/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/more/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/mv/LICENSE b/src/uu/mv/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/mv/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/nice/LICENSE b/src/uu/nice/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/nice/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/nl/LICENSE b/src/uu/nl/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/nl/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/nohup/LICENSE b/src/uu/nohup/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/nohup/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/nproc/LICENSE b/src/uu/nproc/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/nproc/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/numfmt/LICENSE b/src/uu/numfmt/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/numfmt/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/od/LICENSE b/src/uu/od/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/od/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/paste/LICENSE b/src/uu/paste/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/paste/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/pathchk/LICENSE b/src/uu/pathchk/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/pathchk/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/pinky/LICENSE b/src/uu/pinky/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/pinky/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/pr/LICENSE b/src/uu/pr/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/pr/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/printenv/LICENSE b/src/uu/printenv/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/printenv/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/printf/LICENSE b/src/uu/printf/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/printf/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/ptx/LICENSE b/src/uu/ptx/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/ptx/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/pwd/LICENSE b/src/uu/pwd/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/pwd/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/readlink/LICENSE b/src/uu/readlink/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/readlink/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/realpath/LICENSE b/src/uu/realpath/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/realpath/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/relpath/LICENSE b/src/uu/relpath/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/relpath/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/rm/LICENSE b/src/uu/rm/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/rm/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/rmdir/LICENSE b/src/uu/rmdir/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/rmdir/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/runcon/LICENSE b/src/uu/runcon/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/runcon/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/seq/LICENSE b/src/uu/seq/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/seq/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/shred/LICENSE b/src/uu/shred/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/shred/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/shuf/LICENSE b/src/uu/shuf/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/shuf/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/sleep/LICENSE b/src/uu/sleep/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/sleep/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/sort/LICENSE b/src/uu/sort/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/sort/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/split/LICENSE b/src/uu/split/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/split/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/stat/LICENSE b/src/uu/stat/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/stat/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/stdbuf/LICENSE b/src/uu/stdbuf/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/stdbuf/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/sum/LICENSE b/src/uu/sum/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/sum/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/sync/LICENSE b/src/uu/sync/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/sync/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/tac/LICENSE b/src/uu/tac/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/tac/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/tail/LICENSE b/src/uu/tail/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/tail/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/tee/LICENSE b/src/uu/tee/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/tee/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/test/LICENSE b/src/uu/test/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/test/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/timeout/LICENSE b/src/uu/timeout/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/timeout/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/touch/LICENSE b/src/uu/touch/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/touch/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/tr/LICENSE b/src/uu/tr/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/tr/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/true/LICENSE b/src/uu/true/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/true/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/truncate/LICENSE b/src/uu/truncate/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/truncate/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/tsort/LICENSE b/src/uu/tsort/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/tsort/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/tty/LICENSE b/src/uu/tty/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/tty/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/uname/LICENSE b/src/uu/uname/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/uname/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/unexpand/LICENSE b/src/uu/unexpand/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/unexpand/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/uniq/LICENSE b/src/uu/uniq/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/uniq/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/unlink/LICENSE b/src/uu/unlink/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/unlink/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/uptime/LICENSE b/src/uu/uptime/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/uptime/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/users/LICENSE b/src/uu/users/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/users/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/wc/LICENSE b/src/uu/wc/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/wc/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/who/LICENSE b/src/uu/who/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/who/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/whoami/LICENSE b/src/uu/whoami/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/whoami/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uu/yes/LICENSE b/src/uu/yes/LICENSE new file mode 120000 index 000000000..5853aaea5 --- /dev/null +++ b/src/uu/yes/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/src/uucore/LICENSE b/src/uucore/LICENSE new file mode 120000 index 000000000..30cff7403 --- /dev/null +++ b/src/uucore/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/src/uucore_procs/LICENSE b/src/uucore_procs/LICENSE new file mode 120000 index 000000000..30cff7403 --- /dev/null +++ b/src/uucore_procs/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file From 281ec190e0688b30619cb6d05c17426440ca10f4 Mon Sep 17 00:00:00 2001 From: xxyzz Date: Sat, 12 Feb 2022 20:10:17 +0800 Subject: [PATCH 15/60] chmod: replace walkdir with std::fs walkdir filters some files that need to be modified, for example, files inside an unreadable folder. --- Cargo.lock | 1 - src/uu/chmod/Cargo.toml | 1 - src/uu/chmod/src/chmod.rs | 16 +++++++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 805c1c83a..fd036ac64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2385,7 +2385,6 @@ dependencies = [ "clap 3.0.10", "libc", "uucore", - "walkdir", ] [[package]] diff --git a/src/uu/chmod/Cargo.toml b/src/uu/chmod/Cargo.toml index 237368cf4..07cf65d15 100644 --- a/src/uu/chmod/Cargo.toml +++ b/src/uu/chmod/Cargo.toml @@ -18,7 +18,6 @@ path = "src/chmod.rs" clap = { version = "3.0", features = ["wrap_help", "cargo"] } libc = "0.2.42" uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs", "mode"] } -walkdir = "2.2" [[bin]] name = "chmod" diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index 84c5850d6..19c4eced2 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -18,7 +18,6 @@ use uucore::libc::mode_t; #[cfg(not(windows))] use uucore::mode; use uucore::{format_usage, show_error, InvalidEncodingHandling}; -use walkdir::WalkDir; static ABOUT: &str = "Change the mode of each FILE to MODE. With --reference, change the mode of each FILE to that of RFILE."; @@ -227,10 +226,17 @@ impl Chmoder { if !self.recursive { r = self.chmod_file(file).and(r); } else { - for entry in WalkDir::new(&filename).into_iter().filter_map(|e| e.ok()) { - let file = entry.path(); - r = self.chmod_file(file).and(r); - } + r = self.walk_dir(file); + } + } + r + } + + fn walk_dir(&self, file_path: &Path) -> UResult<()> { + let mut r = self.chmod_file(file_path); + if file_path.is_dir() { + for dir_entry in fs::read_dir(file_path)? { + r = self.walk_dir(dir_entry?.path().as_path()); } } r From 9083f236da43175a78908da3c9735338c06287c6 Mon Sep 17 00:00:00 2001 From: xxyzz Date: Sun, 13 Feb 2022 08:55:58 +0800 Subject: [PATCH 16/60] test_chmod: add read permission recursively test --- tests/by-util/test_chmod.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index f3c01722e..bc5fd97e9 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -356,6 +356,26 @@ fn test_chmod_recursive() { } } +#[test] +#[allow(clippy::unreadable_literal)] +fn test_chmod_recursive_read_permission() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir("a"); + at.mkdir("a/b"); + let mut perms = at.metadata("a/b").permissions(); + perms.set_mode(0o311); + set_permissions(at.plus_as_string("a/b"), perms.clone()).unwrap(); + set_permissions(at.plus_as_string("a"), perms).unwrap(); + + ucmd.arg("-R") + .arg("u+r") + .arg("a") + .succeeds(); + + assert_eq!(at.metadata("a").permissions().mode(), 0o40711); + assert_eq!(at.metadata("a/b").permissions().mode(), 0o40711); +} + #[test] fn test_chmod_non_existing_file() { new_ucmd!() From 3272a590dbfa30eece811c12d801218c6f6f1a79 Mon Sep 17 00:00:00 2001 From: xxyzz Date: Mon, 14 Feb 2022 08:26:14 +0800 Subject: [PATCH 17/60] test_chmod: update expected results of test_chmod_recursive The test succeeds before because the files are read before the permission change and that's inconsistent with GNU chmod. --- tests/by-util/test_chmod.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index bc5fd97e9..6cbe7e887 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -334,19 +334,20 @@ fn test_chmod_recursive() { make_file(&at.plus_as_string("a/b/c/c"), 0o100444); make_file(&at.plus_as_string("z/y"), 0o100444); + // only the permissions of folder `a` and `z` are changed + // folder can't be read after read permission is removed ucmd.arg("-R") .arg("--verbose") .arg("-r,a+w") .arg("a") .arg("z") - .succeeds() - .stdout_contains(&"to 0333 (-wx-wx-wx)") - .stdout_contains(&"to 0222 (-w--w--w-)"); + .fails() + .stderr_is("chmod: Permission denied"); - assert_eq!(at.metadata("z/y").permissions().mode(), 0o100222); - assert_eq!(at.metadata("a/a").permissions().mode(), 0o100222); - assert_eq!(at.metadata("a/b/b").permissions().mode(), 0o100222); - assert_eq!(at.metadata("a/b/c/c").permissions().mode(), 0o100222); + assert_eq!(at.metadata("z/y").permissions().mode(), 0o100444); + assert_eq!(at.metadata("a/a").permissions().mode(), 0o100444); + assert_eq!(at.metadata("a/b/b").permissions().mode(), 0o100444); + assert_eq!(at.metadata("a/b/c/c").permissions().mode(), 0o100444); println!("mode {:o}", at.metadata("a").permissions().mode()); assert_eq!(at.metadata("a").permissions().mode(), 0o40333); assert_eq!(at.metadata("z").permissions().mode(), 0o40333); @@ -367,10 +368,7 @@ fn test_chmod_recursive_read_permission() { set_permissions(at.plus_as_string("a/b"), perms.clone()).unwrap(); set_permissions(at.plus_as_string("a"), perms).unwrap(); - ucmd.arg("-R") - .arg("u+r") - .arg("a") - .succeeds(); + ucmd.arg("-R").arg("u+r").arg("a").succeeds(); assert_eq!(at.metadata("a").permissions().mode(), 0o40711); assert_eq!(at.metadata("a/b").permissions().mode(), 0o40711); From ce385be575fed805dad7f5fc96efe92db1467284 Mon Sep 17 00:00:00 2001 From: xxyzz Date: Sun, 6 Mar 2022 09:38:39 +0800 Subject: [PATCH 18/60] chmod: ignore symbolic links during recursive directory traversal --- src/uu/chmod/src/chmod.rs | 9 ++++++--- tests/by-util/test_chmod.rs | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index 19c4eced2..c2b51ae5e 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -234,9 +234,12 @@ impl Chmoder { fn walk_dir(&self, file_path: &Path) -> UResult<()> { let mut r = self.chmod_file(file_path); - if file_path.is_dir() { - for dir_entry in fs::read_dir(file_path)? { - r = self.walk_dir(dir_entry?.path().as_path()); + if !is_symlink(file_path) && file_path.is_dir() { + for dir_entry in file_path.read_dir()? { + let path = dir_entry?.path(); + if !is_symlink(&path) { + r = self.walk_dir(path.as_path()); + } } } r diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index 6cbe7e887..373ad97ce 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -473,8 +473,8 @@ fn test_chmod_symlink_non_existing_file_recursive() { let expected_stdout = &format!( // spell-checker:disable-next-line - "mode of '{}' retained as 0755 (rwxr-xr-x)\nneither symbolic link '{}/{}' nor referent has been changed", - test_directory, test_directory, test_symlink + "mode of '{}' retained as 0755 (rwxr-xr-x)", + test_directory ); // '-v': this should succeed without stderr From 8ccc45c68ca2c7ad937546667e8ef88c1ee0a650 Mon Sep 17 00:00:00 2001 From: 353fc443 aka Seagull <85840256+353fc443@users.noreply.github.com> Date: Sun, 6 Mar 2022 20:36:08 +0000 Subject: [PATCH 19/60] mkdir: recursive reporting of created directories in verbose mode (#3217) --- src/uu/mkdir/src/mkdir.rs | 58 +++++++++++++++++++++++++------------ tests/by-util/test_mkdir.rs | 23 +++++++++++++++ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/uu/mkdir/src/mkdir.rs b/src/uu/mkdir/src/mkdir.rs index b225bb300..14b1ebaf5 100644 --- a/src/uu/mkdir/src/mkdir.rs +++ b/src/uu/mkdir/src/mkdir.rs @@ -11,10 +11,11 @@ extern crate uucore; use clap::{crate_version, App, AppSettings, Arg, ArgMatches, OsValues}; -use std::fs; use std::path::Path; use uucore::display::Quotable; -use uucore::error::{FromIo, UResult, USimpleError}; +#[cfg(not(windows))] +use uucore::error::FromIo; +use uucore::error::{UResult, USimpleError}; #[cfg(not(windows))] use uucore::mode; use uucore::{format_usage, InvalidEncodingHandling}; @@ -149,22 +150,7 @@ fn exec(dirs: OsValues, recursive: bool, mode: u32, verbose: bool) -> UResult<() } fn mkdir(path: &Path, recursive: bool, mode: u32, verbose: bool) -> UResult<()> { - let create_dir = if recursive { - fs::create_dir_all - } else { - fs::create_dir - }; - - create_dir(path).map_err_context(|| format!("cannot create directory {}", path.quote()))?; - - if verbose { - println!( - "{}: created directory {}", - uucore::util_name(), - path.quote() - ); - } - + create_dir(path, recursive, verbose)?; chmod(path, mode) } @@ -184,3 +170,39 @@ fn chmod(_path: &Path, _mode: u32) -> UResult<()> { // chmod on Windows only sets the readonly flag, which isn't even honored on directories Ok(()) } + +fn create_dir(path: &Path, recursive: bool, verbose: bool) -> UResult<()> { + if path.exists() && !recursive { + return Err(USimpleError::new( + 1, + format!("{}: File exists", path.display()), + )); + } + if path == Path::new("") { + return Ok(()); + } + + if recursive { + match path.parent() { + Some(p) => create_dir(p, recursive, verbose)?, + None => { + USimpleError::new(1, "failed to create whole tree"); + } + } + } + + match std::fs::create_dir(path) { + Ok(()) => { + if verbose { + println!( + "{}: created directory {}", + uucore::util_name(), + path.quote() + ); + } + Ok(()) + } + Err(_) if path.is_dir() => Ok(()), + Err(e) => Err(e.into()), + } +} diff --git a/tests/by-util/test_mkdir.rs b/tests/by-util/test_mkdir.rs index e1038003b..011c7b25a 100644 --- a/tests/by-util/test_mkdir.rs +++ b/tests/by-util/test_mkdir.rs @@ -9,6 +9,8 @@ static TEST_DIR4: &str = "mkdir_test4/mkdir_test4_1"; static TEST_DIR5: &str = "mkdir_test5/mkdir_test5_1"; static TEST_DIR6: &str = "mkdir_test6"; static TEST_FILE7: &str = "mkdir_test7"; +static TEST_DIR8: &str = "mkdir_test8/mkdir_test8_1/mkdir_test8_2"; +static TEST_DIR9: &str = "mkdir_test9/../mkdir_test9_1/../mkdir_test9_2"; #[test] fn test_mkdir_mkdir() { @@ -100,3 +102,24 @@ fn test_multi_symbolic() { let perms = at.metadata(TEST_DIR1).permissions().mode(); assert_eq!(perms, 0o40750); } + +#[test] +fn test_recursive_reporting() { + new_ucmd!() + .arg("-p") + .arg("-v") + .arg(TEST_DIR8) + .succeeds() + .stdout_contains("created directory 'mkdir_test8'") + .stdout_contains("created directory 'mkdir_test8/mkdir_test8_1'") + .stdout_contains("created directory 'mkdir_test8/mkdir_test8_1/mkdir_test8_2'"); + new_ucmd!().arg("-v").arg(TEST_DIR8).fails().no_stdout(); + new_ucmd!() + .arg("-p") + .arg("-v") + .arg(TEST_DIR9) + .succeeds() + .stdout_contains("created directory 'mkdir_test9'") + .stdout_contains("created directory 'mkdir_test9/../mkdir_test9_1'") + .stdout_contains("created directory 'mkdir_test9/../mkdir_test9_1/../mkdir_test9_2'"); +} From 088676efda91dd06d6d62e436979fb131e4e23be Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 6 Mar 2022 22:09:56 +0100 Subject: [PATCH 20/60] update of the doc: master => main --- CONTRIBUTING.md | 2 +- README.md | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2a8a80315..ac2e0811e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to coreutils -Contributions are very welcome, and should target Rust's master branch until the +Contributions are very welcome, and should target Rust's main branch until the standard libraries are stabilized. You may *claim* an item on the to-do list by following these steps: diff --git a/README.md b/README.md index d30fd4c05..ae217dc49 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Crates.io](https://img.shields.io/crates/v/coreutils.svg)](https://crates.io/crates/coreutils) [![Discord](https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat)](https://discord.gg/wQVJbvJ) -[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/coreutils/blob/master/LICENSE) +[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/coreutils/blob/main/LICENSE) [![LOC](https://tokei.rs/b1/github/uutils/coreutils?category=code)](https://github.com/Aaronepower/tokei) [![dependency status](https://deps.rs/repo/github/uutils/coreutils/status.svg)](https://deps.rs/repo/github/uutils/coreutils) @@ -410,12 +410,12 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md). | link | | | | ln | | | | logname | | | -| ~~md5sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | | -| ~~sha1sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | | -| ~~sha224sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | | -| ~~sha256sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | | -| ~~sha384sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | | -| ~~sha512sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | | +| ~~md5sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | | +| ~~sha1sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | | +| ~~sha224sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | | +| ~~sha256sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | | +| ~~sha384sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | | +| ~~sha512sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/main/src/uu/hashsum/src/hashsum.rs)) | | | | mkdir | | | | mkfifo | | | | mknod | | | From 54a10e955aac8c3461489ff817a2b1aae922ec96 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 6 Mar 2022 22:10:31 +0100 Subject: [PATCH 21/60] Update of the cargo.lock url to point to the right branch --- src/uu/arch/Cargo.toml | 2 +- src/uu/base32/Cargo.toml | 2 +- src/uu/base64/Cargo.toml | 2 +- src/uu/basename/Cargo.toml | 2 +- src/uu/basenc/Cargo.toml | 2 +- src/uu/cat/Cargo.toml | 2 +- src/uu/chcon/Cargo.toml | 2 +- src/uu/chgrp/Cargo.toml | 2 +- src/uu/chmod/Cargo.toml | 2 +- src/uu/chown/Cargo.toml | 2 +- src/uu/chroot/Cargo.toml | 2 +- src/uu/cksum/Cargo.toml | 2 +- src/uu/comm/Cargo.toml | 2 +- src/uu/cp/Cargo.toml | 2 +- src/uu/csplit/Cargo.toml | 2 +- src/uu/cut/Cargo.toml | 2 +- src/uu/date/Cargo.toml | 2 +- src/uu/dd/Cargo.toml | 2 +- src/uu/df/Cargo.toml | 2 +- src/uu/dircolors/Cargo.toml | 2 +- src/uu/dirname/Cargo.toml | 2 +- src/uu/du/Cargo.toml | 2 +- src/uu/echo/Cargo.toml | 2 +- src/uu/env/Cargo.toml | 2 +- src/uu/expand/Cargo.toml | 2 +- src/uu/expr/Cargo.toml | 2 +- src/uu/factor/Cargo.toml | 2 +- src/uu/false/Cargo.toml | 2 +- src/uu/fmt/Cargo.toml | 2 +- src/uu/fold/Cargo.toml | 2 +- src/uu/groups/Cargo.toml | 2 +- src/uu/hashsum/Cargo.toml | 2 +- src/uu/head/Cargo.toml | 2 +- src/uu/hostid/Cargo.toml | 2 +- src/uu/hostname/Cargo.toml | 2 +- src/uu/id/Cargo.toml | 2 +- src/uu/install/Cargo.toml | 2 +- src/uu/join/Cargo.toml | 2 +- src/uu/kill/Cargo.toml | 2 +- src/uu/link/Cargo.toml | 2 +- src/uu/ln/Cargo.toml | 2 +- src/uu/logname/Cargo.toml | 2 +- src/uu/ls/Cargo.toml | 2 +- src/uu/mkdir/Cargo.toml | 2 +- src/uu/mkfifo/Cargo.toml | 2 +- src/uu/mknod/Cargo.toml | 2 +- src/uu/mktemp/Cargo.toml | 2 +- src/uu/more/Cargo.toml | 2 +- src/uu/mv/Cargo.toml | 2 +- src/uu/nice/Cargo.toml | 2 +- src/uu/nl/Cargo.toml | 2 +- src/uu/nohup/Cargo.toml | 2 +- src/uu/nproc/Cargo.toml | 2 +- src/uu/numfmt/Cargo.toml | 2 +- src/uu/od/Cargo.toml | 2 +- src/uu/paste/Cargo.toml | 2 +- src/uu/pathchk/Cargo.toml | 2 +- src/uu/pinky/Cargo.toml | 2 +- src/uu/pr/Cargo.toml | 2 +- src/uu/printenv/Cargo.toml | 2 +- src/uu/printf/Cargo.toml | 2 +- src/uu/ptx/Cargo.toml | 2 +- src/uu/pwd/Cargo.toml | 2 +- src/uu/readlink/Cargo.toml | 2 +- src/uu/realpath/Cargo.toml | 2 +- src/uu/relpath/Cargo.toml | 2 +- src/uu/rm/Cargo.toml | 2 +- src/uu/rmdir/Cargo.toml | 2 +- src/uu/runcon/Cargo.toml | 2 +- src/uu/seq/Cargo.toml | 2 +- src/uu/shred/Cargo.toml | 2 +- src/uu/shuf/Cargo.toml | 2 +- src/uu/sleep/Cargo.toml | 2 +- src/uu/sort/Cargo.toml | 2 +- src/uu/split/Cargo.toml | 2 +- src/uu/stat/Cargo.toml | 2 +- src/uu/stdbuf/Cargo.toml | 2 +- src/uu/stdbuf/src/libstdbuf/Cargo.toml | 2 +- src/uu/sum/Cargo.toml | 2 +- src/uu/sync/Cargo.toml | 2 +- src/uu/tac/Cargo.toml | 2 +- src/uu/tail/Cargo.toml | 2 +- src/uu/tee/Cargo.toml | 2 +- src/uu/test/Cargo.toml | 2 +- src/uu/timeout/Cargo.toml | 2 +- src/uu/touch/Cargo.toml | 2 +- src/uu/tr/Cargo.toml | 2 +- src/uu/true/Cargo.toml | 2 +- src/uu/truncate/Cargo.toml | 2 +- src/uu/tsort/Cargo.toml | 2 +- src/uu/tty/Cargo.toml | 2 +- src/uu/uname/Cargo.toml | 2 +- src/uu/unexpand/Cargo.toml | 2 +- src/uu/uniq/Cargo.toml | 2 +- src/uu/unlink/Cargo.toml | 2 +- src/uu/uptime/Cargo.toml | 2 +- src/uu/users/Cargo.toml | 2 +- src/uu/wc/Cargo.toml | 2 +- src/uu/who/Cargo.toml | 2 +- src/uu/whoami/Cargo.toml | 2 +- src/uu/yes/Cargo.toml | 2 +- src/uucore/Cargo.toml | 2 +- src/uucore_procs/Cargo.toml | 2 +- 103 files changed, 103 insertions(+), 103 deletions(-) diff --git a/src/uu/arch/Cargo.toml b/src/uu/arch/Cargo.toml index 85888c498..207100f59 100644 --- a/src/uu/arch/Cargo.toml +++ b/src/uu/arch/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "arch ~ (uutils) display machine architecture" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/arch" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/arch" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/base32/Cargo.toml b/src/uu/base32/Cargo.toml index cb8ccc3aa..0b695f0aa 100644 --- a/src/uu/base32/Cargo.toml +++ b/src/uu/base32/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "base32 ~ (uutils) decode/encode input (base32-encoding)" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/base32" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/base32" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/base64/Cargo.toml b/src/uu/base64/Cargo.toml index d30df608b..2e46ca8d5 100644 --- a/src/uu/base64/Cargo.toml +++ b/src/uu/base64/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "base64 ~ (uutils) decode/encode input (base64-encoding)" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/base64" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/base64" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/basename/Cargo.toml b/src/uu/basename/Cargo.toml index da93877c0..6426e5046 100644 --- a/src/uu/basename/Cargo.toml +++ b/src/uu/basename/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "basename ~ (uutils) display PATHNAME with leading directory components removed" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/basename" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/basename" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/basenc/Cargo.toml b/src/uu/basenc/Cargo.toml index 07a7b7f67..a0f652cee 100644 --- a/src/uu/basenc/Cargo.toml +++ b/src/uu/basenc/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "basenc ~ (uutils) decode/encode input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/basenc" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/basenc" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/cat/Cargo.toml b/src/uu/cat/Cargo.toml index e1e95bc76..3eef5598b 100644 --- a/src/uu/cat/Cargo.toml +++ b/src/uu/cat/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "cat ~ (uutils) concatenate and display input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/cat" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cat" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/chcon/Cargo.toml b/src/uu/chcon/Cargo.toml index a0a526d3b..75ce3fbd9 100644 --- a/src/uu/chcon/Cargo.toml +++ b/src/uu/chcon/Cargo.toml @@ -5,7 +5,7 @@ authors = ["uutils developers"] license = "MIT" description = "chcon ~ (uutils) change file security context" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chcon" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chcon" keywords = ["coreutils", "uutils", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/chgrp/Cargo.toml b/src/uu/chgrp/Cargo.toml index 3800daa6c..ff60de836 100644 --- a/src/uu/chgrp/Cargo.toml +++ b/src/uu/chgrp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "chgrp ~ (uutils) change the group ownership of FILE" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chgrp" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chgrp" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/chmod/Cargo.toml b/src/uu/chmod/Cargo.toml index 237368cf4..6c7dd7a06 100644 --- a/src/uu/chmod/Cargo.toml +++ b/src/uu/chmod/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "chmod ~ (uutils) change mode of FILE" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chmod" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chmod" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/chown/Cargo.toml b/src/uu/chown/Cargo.toml index ed84d3fbb..b18310646 100644 --- a/src/uu/chown/Cargo.toml +++ b/src/uu/chown/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "chown ~ (uutils) change the ownership of FILE" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chown" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chown" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/chroot/Cargo.toml b/src/uu/chroot/Cargo.toml index ef745009c..d6a9dd080 100644 --- a/src/uu/chroot/Cargo.toml +++ b/src/uu/chroot/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "chroot ~ (uutils) run COMMAND under a new root directory" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/chroot" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/chroot" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/cksum/Cargo.toml b/src/uu/cksum/Cargo.toml index 5a6076648..6643a2bab 100644 --- a/src/uu/cksum/Cargo.toml +++ b/src/uu/cksum/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "cksum ~ (uutils) display CRC and size of input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/cksum" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cksum" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/comm/Cargo.toml b/src/uu/comm/Cargo.toml index 8d9babdcb..58868dfc5 100644 --- a/src/uu/comm/Cargo.toml +++ b/src/uu/comm/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "comm ~ (uutils) compare sorted inputs" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/comm" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/comm" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/cp/Cargo.toml b/src/uu/cp/Cargo.toml index 221c02465..ffc008503 100644 --- a/src/uu/cp/Cargo.toml +++ b/src/uu/cp/Cargo.toml @@ -10,7 +10,7 @@ license = "MIT" description = "cp ~ (uutils) copy SOURCE to DESTINATION" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/cp" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cp" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/csplit/Cargo.toml b/src/uu/csplit/Cargo.toml index 79927594b..89fc118dd 100644 --- a/src/uu/csplit/Cargo.toml +++ b/src/uu/csplit/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "csplit ~ (uutils) Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/ls" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/cut/Cargo.toml b/src/uu/cut/Cargo.toml index dd27fa435..302f8f0c1 100644 --- a/src/uu/cut/Cargo.toml +++ b/src/uu/cut/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "cut ~ (uutils) display byte/field columns of input lines" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/cut" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/cut" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/date/Cargo.toml b/src/uu/date/Cargo.toml index 4ca628998..793022992 100644 --- a/src/uu/date/Cargo.toml +++ b/src/uu/date/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "date ~ (uutils) display or set the current time" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/date" +repository = "https://github.com/uutils/coreutils/tree/main/src/date" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index afb61aded..b0392e14d 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "dd ~ (uutils) copy and convert files" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/dd" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dd" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/df/Cargo.toml b/src/uu/df/Cargo.toml index d3d2fd395..29f32c6cf 100644 --- a/src/uu/df/Cargo.toml +++ b/src/uu/df/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "df ~ (uutils) display file system information" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/df" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/df" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/dircolors/Cargo.toml b/src/uu/dircolors/Cargo.toml index 403da5171..28a520eef 100644 --- a/src/uu/dircolors/Cargo.toml +++ b/src/uu/dircolors/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "dircolors ~ (uutils) display commands to set LS_COLORS" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/dircolors" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dircolors" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/dirname/Cargo.toml b/src/uu/dirname/Cargo.toml index 9980d31a6..9b85edac0 100644 --- a/src/uu/dirname/Cargo.toml +++ b/src/uu/dirname/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "dirname ~ (uutils) display parent directory of PATHNAME" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/dirname" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/dirname" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/du/Cargo.toml b/src/uu/du/Cargo.toml index 2fb7a0625..a6af1a3ae 100644 --- a/src/uu/du/Cargo.toml +++ b/src/uu/du/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "du ~ (uutils) display disk usage" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/du" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/du" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/echo/Cargo.toml b/src/uu/echo/Cargo.toml index 042a1225a..8b8ba0819 100644 --- a/src/uu/echo/Cargo.toml +++ b/src/uu/echo/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "echo ~ (uutils) display TEXT" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/echo" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/echo" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/env/Cargo.toml b/src/uu/env/Cargo.toml index bb43a75bd..d5537fb54 100644 --- a/src/uu/env/Cargo.toml +++ b/src/uu/env/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "env ~ (uutils) set each NAME to VALUE in the environment and run COMMAND" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/env" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/env" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/expand/Cargo.toml b/src/uu/expand/Cargo.toml index 42a10dabf..ba2cdd317 100644 --- a/src/uu/expand/Cargo.toml +++ b/src/uu/expand/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "expand ~ (uutils) convert input tabs to spaces" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/expand" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/expand" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/expr/Cargo.toml b/src/uu/expr/Cargo.toml index 5d163a55f..278ebe46e 100644 --- a/src/uu/expr/Cargo.toml +++ b/src/uu/expr/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "expr ~ (uutils) display the value of EXPRESSION" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/expr" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/expr" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/factor/Cargo.toml b/src/uu/factor/Cargo.toml index ae9a0969b..aec43af76 100644 --- a/src/uu/factor/Cargo.toml +++ b/src/uu/factor/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "factor ~ (uutils) display the prime factors of each NUMBER" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/false/Cargo.toml b/src/uu/false/Cargo.toml index 7ac5602b7..7cbdcd41c 100644 --- a/src/uu/false/Cargo.toml +++ b/src/uu/false/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "false ~ (uutils) do nothing and fail" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/false" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/false" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/fmt/Cargo.toml b/src/uu/fmt/Cargo.toml index b86c94d86..47574b384 100644 --- a/src/uu/fmt/Cargo.toml +++ b/src/uu/fmt/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "fmt ~ (uutils) reformat each paragraph of input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/fmt" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/fmt" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/fold/Cargo.toml b/src/uu/fold/Cargo.toml index b177eb4f7..40e3fceb0 100644 --- a/src/uu/fold/Cargo.toml +++ b/src/uu/fold/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "fold ~ (uutils) wrap each line of input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/fold" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/fold" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/groups/Cargo.toml b/src/uu/groups/Cargo.toml index 93b67ba43..d7b3bf01f 100644 --- a/src/uu/groups/Cargo.toml +++ b/src/uu/groups/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "groups ~ (uutils) display group memberships for USERNAME" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/groups" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/groups" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/hashsum/Cargo.toml b/src/uu/hashsum/Cargo.toml index 1f8f1b923..d0362e3a3 100644 --- a/src/uu/hashsum/Cargo.toml +++ b/src/uu/hashsum/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "hashsum ~ (uutils) display or check input digests" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/hashsum" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hashsum" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/head/Cargo.toml b/src/uu/head/Cargo.toml index 04a512492..65ac7f7cb 100644 --- a/src/uu/head/Cargo.toml +++ b/src/uu/head/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "head ~ (uutils) display the first lines of input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/head" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/head" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/hostid/Cargo.toml b/src/uu/hostid/Cargo.toml index 309671a29..f0013fb05 100644 --- a/src/uu/hostid/Cargo.toml +++ b/src/uu/hostid/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "hostid ~ (uutils) display the numeric identifier of the current host" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/hostid" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hostid" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/hostname/Cargo.toml b/src/uu/hostname/Cargo.toml index 55fc064f3..8f72dc402 100644 --- a/src/uu/hostname/Cargo.toml +++ b/src/uu/hostname/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "hostname ~ (uutils) display or set the host name of the current host" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/hostname" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/hostname" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/id/Cargo.toml b/src/uu/id/Cargo.toml index b611e1e91..a0ccefbd0 100644 --- a/src/uu/id/Cargo.toml +++ b/src/uu/id/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "id ~ (uutils) display user and group information for USER" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/id" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/id" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/install/Cargo.toml b/src/uu/install/Cargo.toml index d00655e8b..c5ca889ec 100644 --- a/src/uu/install/Cargo.toml +++ b/src/uu/install/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT" description = "install ~ (uutils) copy files from SOURCE to DESTINATION (with specified attributes)" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/install" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/install" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/join/Cargo.toml b/src/uu/join/Cargo.toml index 2a03fe002..706b246c2 100644 --- a/src/uu/join/Cargo.toml +++ b/src/uu/join/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "join ~ (uutils) merge lines from inputs with matching join fields" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/join" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/join" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/kill/Cargo.toml b/src/uu/kill/Cargo.toml index 42c90b89f..46853a85d 100644 --- a/src/uu/kill/Cargo.toml +++ b/src/uu/kill/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "kill ~ (uutils) send a signal to a process" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/kill" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/kill" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/link/Cargo.toml b/src/uu/link/Cargo.toml index aef64847e..46d7d26f0 100644 --- a/src/uu/link/Cargo.toml +++ b/src/uu/link/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "link ~ (uutils) create a hard (file system) link to FILE" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/link" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/link" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/ln/Cargo.toml b/src/uu/ln/Cargo.toml index 6d268408a..b1f42cad0 100644 --- a/src/uu/ln/Cargo.toml +++ b/src/uu/ln/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "ln ~ (uutils) create a (file system) link to TARGET" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/ln" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ln" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/logname/Cargo.toml b/src/uu/logname/Cargo.toml index 5a109491e..8e1241b3b 100644 --- a/src/uu/logname/Cargo.toml +++ b/src/uu/logname/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "logname ~ (uutils) display the login name of the current user" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/logname" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/logname" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index ed38712be..22a5a1aed 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "ls ~ (uutils) display directory contents" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/ls" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/mkdir/Cargo.toml b/src/uu/mkdir/Cargo.toml index a984367be..4bb12e43d 100644 --- a/src/uu/mkdir/Cargo.toml +++ b/src/uu/mkdir/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "mkdir ~ (uutils) create DIRECTORY" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/mkdir" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mkdir" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/mkfifo/Cargo.toml b/src/uu/mkfifo/Cargo.toml index 3a61c55a5..473cc0143 100644 --- a/src/uu/mkfifo/Cargo.toml +++ b/src/uu/mkfifo/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "mkfifo ~ (uutils) create FIFOs (named pipes)" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/mkfifo" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mkfifo" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/mknod/Cargo.toml b/src/uu/mknod/Cargo.toml index 7af88e7b5..9bed05fd4 100644 --- a/src/uu/mknod/Cargo.toml +++ b/src/uu/mknod/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "mknod ~ (uutils) create special file NAME of TYPE" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/mknod" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mknod" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/mktemp/Cargo.toml b/src/uu/mktemp/Cargo.toml index 95ab09aa6..eb25702d3 100644 --- a/src/uu/mktemp/Cargo.toml +++ b/src/uu/mktemp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "mktemp ~ (uutils) create and display a temporary file or directory from TEMPLATE" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/mktemp" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mktemp" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/more/Cargo.toml b/src/uu/more/Cargo.toml index 067dfe8c2..d8e48cbd7 100644 --- a/src/uu/more/Cargo.toml +++ b/src/uu/more/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "more ~ (uutils) input perusal filter" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/more" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/more" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/mv/Cargo.toml b/src/uu/mv/Cargo.toml index fdce84366..79012b5e0 100644 --- a/src/uu/mv/Cargo.toml +++ b/src/uu/mv/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "mv ~ (uutils) move (rename) SOURCE to DESTINATION" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/mv" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/mv" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/nice/Cargo.toml b/src/uu/nice/Cargo.toml index db95dc99a..fc42cb801 100644 --- a/src/uu/nice/Cargo.toml +++ b/src/uu/nice/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "nice ~ (uutils) run PROGRAM with modified scheduling priority" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/nice" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nice" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/nl/Cargo.toml b/src/uu/nl/Cargo.toml index 9817cf6f8..9ce451f27 100644 --- a/src/uu/nl/Cargo.toml +++ b/src/uu/nl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "nl ~ (uutils) display input with added line numbers" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/nl" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nl" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/nohup/Cargo.toml b/src/uu/nohup/Cargo.toml index 13551a361..a19de6eb5 100644 --- a/src/uu/nohup/Cargo.toml +++ b/src/uu/nohup/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "nohup ~ (uutils) run COMMAND, ignoring hangup signals" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/nohup" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nohup" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/nproc/Cargo.toml b/src/uu/nproc/Cargo.toml index cded0c381..ef9ee5b2a 100644 --- a/src/uu/nproc/Cargo.toml +++ b/src/uu/nproc/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "nproc ~ (uutils) display the number of processing units available" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/nproc" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/nproc" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/numfmt/Cargo.toml b/src/uu/numfmt/Cargo.toml index 336513b55..8afc04f11 100644 --- a/src/uu/numfmt/Cargo.toml +++ b/src/uu/numfmt/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "numfmt ~ (uutils) reformat NUMBER" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/numfmt" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/numfmt" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/od/Cargo.toml b/src/uu/od/Cargo.toml index 3da001265..10609ae47 100644 --- a/src/uu/od/Cargo.toml +++ b/src/uu/od/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "od ~ (uutils) display formatted representation of input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/od" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/od" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/paste/Cargo.toml b/src/uu/paste/Cargo.toml index 0a6728ca7..c37c7a939 100644 --- a/src/uu/paste/Cargo.toml +++ b/src/uu/paste/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "paste ~ (uutils) merge lines from inputs" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/paste" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/paste" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/pathchk/Cargo.toml b/src/uu/pathchk/Cargo.toml index d9469d49e..982a4ea89 100644 --- a/src/uu/pathchk/Cargo.toml +++ b/src/uu/pathchk/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "pathchk ~ (uutils) diagnose invalid or non-portable PATHNAME" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/pathchk" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pathchk" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/pinky/Cargo.toml b/src/uu/pinky/Cargo.toml index 05fa71eba..3bbffe981 100644 --- a/src/uu/pinky/Cargo.toml +++ b/src/uu/pinky/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "pinky ~ (uutils) display user information" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/pinky" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pinky" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/pr/Cargo.toml b/src/uu/pr/Cargo.toml index 8e9029daa..100f37998 100644 --- a/src/uu/pr/Cargo.toml +++ b/src/uu/pr/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "pr ~ (uutils) convert text files for printing" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/pr" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pr" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/printenv/Cargo.toml b/src/uu/printenv/Cargo.toml index fbc5094dc..ff8e6ffe1 100644 --- a/src/uu/printenv/Cargo.toml +++ b/src/uu/printenv/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "printenv ~ (uutils) display value of environment VAR" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/printenv" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/printenv" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/printf/Cargo.toml b/src/uu/printf/Cargo.toml index da49875fb..98257f9aa 100644 --- a/src/uu/printf/Cargo.toml +++ b/src/uu/printf/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT" description = "printf ~ (uutils) FORMAT and display ARGUMENTS" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/printf" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/printf" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/ptx/Cargo.toml b/src/uu/ptx/Cargo.toml index 6493cafb0..90285ebf3 100644 --- a/src/uu/ptx/Cargo.toml +++ b/src/uu/ptx/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "ptx ~ (uutils) display a permuted index of input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/ptx" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ptx" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/pwd/Cargo.toml b/src/uu/pwd/Cargo.toml index bb47b9f2d..faf718ebd 100644 --- a/src/uu/pwd/Cargo.toml +++ b/src/uu/pwd/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "pwd ~ (uutils) display current working directory" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/pwd" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/pwd" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/readlink/Cargo.toml b/src/uu/readlink/Cargo.toml index 638d123b5..30bb7e925 100644 --- a/src/uu/readlink/Cargo.toml +++ b/src/uu/readlink/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "readlink ~ (uutils) display resolved path of PATHNAME" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/readlink" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/readlink" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/realpath/Cargo.toml b/src/uu/realpath/Cargo.toml index 38996de69..cabd33256 100644 --- a/src/uu/realpath/Cargo.toml +++ b/src/uu/realpath/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "realpath ~ (uutils) display resolved absolute path of PATHNAME" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/realpath" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/realpath" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/relpath/Cargo.toml b/src/uu/relpath/Cargo.toml index d631fa66c..29eab9ac2 100644 --- a/src/uu/relpath/Cargo.toml +++ b/src/uu/relpath/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "relpath ~ (uutils) display relative path of PATHNAME_TO from PATHNAME_FROM" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/relpath" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/relpath" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/rm/Cargo.toml b/src/uu/rm/Cargo.toml index 190294090..2ef29d435 100644 --- a/src/uu/rm/Cargo.toml +++ b/src/uu/rm/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "rm ~ (uutils) remove PATHNAME" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/rm" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/rm" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/rmdir/Cargo.toml b/src/uu/rmdir/Cargo.toml index 8dcb0cf97..86d5031c8 100644 --- a/src/uu/rmdir/Cargo.toml +++ b/src/uu/rmdir/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "rmdir ~ (uutils) remove empty DIRECTORY" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/rmdir" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/rmdir" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/runcon/Cargo.toml b/src/uu/runcon/Cargo.toml index 7cdb4d9a3..5c68a8d68 100644 --- a/src/uu/runcon/Cargo.toml +++ b/src/uu/runcon/Cargo.toml @@ -5,7 +5,7 @@ authors = ["uutils developers"] license = "MIT" description = "runcon ~ (uutils) run command with specified security context" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/runcon" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/runcon" keywords = ["coreutils", "uutils", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/seq/Cargo.toml b/src/uu/seq/Cargo.toml index e6b5a2863..fe986529d 100644 --- a/src/uu/seq/Cargo.toml +++ b/src/uu/seq/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT" description = "seq ~ (uutils) display a sequence of numbers" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/seq" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/seq" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/shred/Cargo.toml b/src/uu/shred/Cargo.toml index 857e371bb..c73e0741b 100644 --- a/src/uu/shred/Cargo.toml +++ b/src/uu/shred/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "shred ~ (uutils) hide former FILE contents with repeated overwrites" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/shred" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/shred" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/shuf/Cargo.toml b/src/uu/shuf/Cargo.toml index 03dd11abc..ab74e08b8 100644 --- a/src/uu/shuf/Cargo.toml +++ b/src/uu/shuf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "shuf ~ (uutils) display random permutations of input lines" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/shuf" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/shuf" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/sleep/Cargo.toml b/src/uu/sleep/Cargo.toml index 37066f3cf..7f5413a3f 100644 --- a/src/uu/sleep/Cargo.toml +++ b/src/uu/sleep/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "sleep ~ (uutils) pause for DURATION" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/sleep" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/sleep" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 1a99e0445..bd74bce85 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "sort ~ (uutils) sort input lines" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/sort" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/sort" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/split/Cargo.toml b/src/uu/split/Cargo.toml index f6920e797..233563d1c 100644 --- a/src/uu/split/Cargo.toml +++ b/src/uu/split/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "split ~ (uutils) split input into output files" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/split" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/split" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/stat/Cargo.toml b/src/uu/stat/Cargo.toml index e0ed8aa73..ecf5aa5bd 100644 --- a/src/uu/stat/Cargo.toml +++ b/src/uu/stat/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "stat ~ (uutils) display FILE status" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/stat" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/stat" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/stdbuf/Cargo.toml b/src/uu/stdbuf/Cargo.toml index a65d77c76..c7942779d 100644 --- a/src/uu/stdbuf/Cargo.toml +++ b/src/uu/stdbuf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "stdbuf ~ (uutils) run COMMAND with modified standard stream buffering" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/stdbuf" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/stdbuf" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/stdbuf/src/libstdbuf/Cargo.toml b/src/uu/stdbuf/src/libstdbuf/Cargo.toml index 5c8a80f0e..fc623b666 100644 --- a/src/uu/stdbuf/src/libstdbuf/Cargo.toml +++ b/src/uu/stdbuf/src/libstdbuf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "stdbuf/libstdbuf ~ (uutils); dynamic library required for stdbuf" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/stdbuf" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/stdbuf" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/sum/Cargo.toml b/src/uu/sum/Cargo.toml index 7bc4468d0..56a4edf9a 100644 --- a/src/uu/sum/Cargo.toml +++ b/src/uu/sum/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "sum ~ (uutils) display checksum and block counts for input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/sum" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/sum" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/sync/Cargo.toml b/src/uu/sync/Cargo.toml index 364ab181b..a84d8c192 100644 --- a/src/uu/sync/Cargo.toml +++ b/src/uu/sync/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "sync ~ (uutils) synchronize cache writes to storage" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/sync" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/sync" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/tac/Cargo.toml b/src/uu/tac/Cargo.toml index 1c5242ba7..a430c1e53 100644 --- a/src/uu/tac/Cargo.toml +++ b/src/uu/tac/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT" description = "tac ~ (uutils) concatenate and display input lines in reverse order" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/tac" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/tac" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/tail/Cargo.toml b/src/uu/tail/Cargo.toml index 4f40431b1..7abc10a56 100644 --- a/src/uu/tail/Cargo.toml +++ b/src/uu/tail/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "tail ~ (uutils) display the last lines of input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/tail" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/tail" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/tee/Cargo.toml b/src/uu/tee/Cargo.toml index 054e3592e..b71e9b0ef 100644 --- a/src/uu/tee/Cargo.toml +++ b/src/uu/tee/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "tee ~ (uutils) display input and copy to FILE" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/tee" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/tee" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/test/Cargo.toml b/src/uu/test/Cargo.toml index 0acad31ab..bf0558a7a 100644 --- a/src/uu/test/Cargo.toml +++ b/src/uu/test/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "test ~ (uutils) evaluate comparison and file type expressions" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/test" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/test" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/timeout/Cargo.toml b/src/uu/timeout/Cargo.toml index ae0cb1a0b..c250381e8 100644 --- a/src/uu/timeout/Cargo.toml +++ b/src/uu/timeout/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "timeout ~ (uutils) run COMMAND with a DURATION time limit" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/timeout" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/timeout" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/touch/Cargo.toml b/src/uu/touch/Cargo.toml index dd024eacd..606a7ecc4 100644 --- a/src/uu/touch/Cargo.toml +++ b/src/uu/touch/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "touch ~ (uutils) change FILE timestamps" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/touch" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/touch" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/tr/Cargo.toml b/src/uu/tr/Cargo.toml index bfccfb4e7..4ecfe2045 100644 --- a/src/uu/tr/Cargo.toml +++ b/src/uu/tr/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "tr ~ (uutils) translate characters within input and display" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/tr" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/tr" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/true/Cargo.toml b/src/uu/true/Cargo.toml index 82023585f..c737d2234 100644 --- a/src/uu/true/Cargo.toml +++ b/src/uu/true/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "true ~ (uutils) do nothing and succeed" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/true" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/true" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/truncate/Cargo.toml b/src/uu/truncate/Cargo.toml index 402c201f6..3048df521 100644 --- a/src/uu/truncate/Cargo.toml +++ b/src/uu/truncate/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "truncate ~ (uutils) truncate (or extend) FILE to SIZE" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/truncate" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/truncate" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/tsort/Cargo.toml b/src/uu/tsort/Cargo.toml index 8964486bf..39e73f0a1 100644 --- a/src/uu/tsort/Cargo.toml +++ b/src/uu/tsort/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "tsort ~ (uutils) topologically sort input (partially ordered) pairs" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/tsort" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/tsort" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/tty/Cargo.toml b/src/uu/tty/Cargo.toml index bdb7ebe9c..43a0d2edc 100644 --- a/src/uu/tty/Cargo.toml +++ b/src/uu/tty/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "tty ~ (uutils) display the name of the terminal connected to standard input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/tty" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/tty" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/uname/Cargo.toml b/src/uu/uname/Cargo.toml index b840b7584..4a68e1b64 100644 --- a/src/uu/uname/Cargo.toml +++ b/src/uu/uname/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "uname ~ (uutils) display system information" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/uname" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/uname" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/unexpand/Cargo.toml b/src/uu/unexpand/Cargo.toml index b679d786e..86de08b9f 100644 --- a/src/uu/unexpand/Cargo.toml +++ b/src/uu/unexpand/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "unexpand ~ (uutils) convert input spaces to tabs" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/unexpand" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/unexpand" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/uniq/Cargo.toml b/src/uu/uniq/Cargo.toml index 5d4a085c7..a6c9810b9 100644 --- a/src/uu/uniq/Cargo.toml +++ b/src/uu/uniq/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "uniq ~ (uutils) filter identical adjacent lines from input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/uniq" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/uniq" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/unlink/Cargo.toml b/src/uu/unlink/Cargo.toml index 0869427e9..8d3ac6b26 100644 --- a/src/uu/unlink/Cargo.toml +++ b/src/uu/unlink/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "unlink ~ (uutils) remove a (file system) link to FILE" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/unlink" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/unlink" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/uptime/Cargo.toml b/src/uu/uptime/Cargo.toml index fcce5bb47..8edb568d3 100644 --- a/src/uu/uptime/Cargo.toml +++ b/src/uu/uptime/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "uptime ~ (uutils) display dynamic system information" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/uptime" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/uptime" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/users/Cargo.toml b/src/uu/users/Cargo.toml index 5125bcc33..dfd6d0309 100644 --- a/src/uu/users/Cargo.toml +++ b/src/uu/users/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "users ~ (uutils) display names of currently logged-in users" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/users" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/users" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/wc/Cargo.toml b/src/uu/wc/Cargo.toml index 28520f1d7..0e998b74e 100644 --- a/src/uu/wc/Cargo.toml +++ b/src/uu/wc/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "wc ~ (uutils) display newline, word, and byte counts for input" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/wc" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/wc" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/who/Cargo.toml b/src/uu/who/Cargo.toml index 9559bdc3c..c0fd70ae8 100644 --- a/src/uu/who/Cargo.toml +++ b/src/uu/who/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "who ~ (uutils) display information about currently logged-in users" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/who" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/who" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/whoami/Cargo.toml b/src/uu/whoami/Cargo.toml index 8098e8065..151ef1141 100644 --- a/src/uu/whoami/Cargo.toml +++ b/src/uu/whoami/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "whoami ~ (uutils) display user name of current effective user ID" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/whoami" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/whoami" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uu/yes/Cargo.toml b/src/uu/yes/Cargo.toml index 131b999db..2054274f8 100644 --- a/src/uu/yes/Cargo.toml +++ b/src/uu/yes/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "yes ~ (uutils) repeatedly display a line with STRING (or 'y')" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uu/yes" +repository = "https://github.com/uutils/coreutils/tree/main/src/uu/yes" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] edition = "2018" diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 949ec25f6..a8d992fbb 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "uutils ~ 'core' uutils code library (cross-platform)" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uucore" +repository = "https://github.com/uutils/coreutils/tree/main/src/uucore" # readme = "README.md" keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] categories = ["command-line-utilities"] diff --git a/src/uucore_procs/Cargo.toml b/src/uucore_procs/Cargo.toml index 2da22dbac..3760fe7f7 100644 --- a/src/uucore_procs/Cargo.toml +++ b/src/uucore_procs/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT" description = "uutils ~ 'uucore' proc-macros" homepage = "https://github.com/uutils/coreutils" -repository = "https://github.com/uutils/coreutils/tree/master/src/uucore_procs" +repository = "https://github.com/uutils/coreutils/tree/main/src/uucore_procs" # readme = "README.md" keywords = ["cross-platform", "proc-macros", "uucore", "uutils"] # categories = ["os"] From 90cc2bff6ae078a8da5520e2315608d96c046144 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 6 Mar 2022 22:15:49 +0100 Subject: [PATCH 22/60] Update of the license as many others contributed --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 0177c4ab7..49fdbd4cf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) Jordi Boggiano +Copyright (c) Jordi Boggiano and many others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 103dffc12e408a1f2b02c3a1587394077d6b8b95 Mon Sep 17 00:00:00 2001 From: Devin Gunay Date: Sun, 6 Mar 2022 14:00:42 -0800 Subject: [PATCH 23/60] touch: implement `-` (#3158) --- Cargo.lock | 1 + src/uu/touch/Cargo.toml | 3 ++ src/uu/touch/src/touch.rs | 100 ++++++++++++++++++++++++++++++++++-- tests/by-util/test_touch.rs | 21 ++++++++ 4 files changed, 122 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 805c1c83a..81c35d28b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3171,6 +3171,7 @@ dependencies = [ "filetime", "time", "uucore", + "winapi 0.3.9", ] [[package]] diff --git a/src/uu/touch/Cargo.toml b/src/uu/touch/Cargo.toml index 606a7ecc4..bb6e0dc1e 100644 --- a/src/uu/touch/Cargo.toml +++ b/src/uu/touch/Cargo.toml @@ -20,6 +20,9 @@ clap = { version = "3.0", features = ["wrap_help", "cargo"] } time = "0.1.40" uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["libc"] } +[target.'cfg(target_os = "windows")'.dependencies] +winapi = { version = "0.3" } + [[bin]] name = "touch" path = "src/main.rs" diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 620538bbf..2b56cdb95 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -6,7 +6,7 @@ // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. -// spell-checker:ignore (ToDO) filetime strptime utcoff strs datetime MMDDhhmm clapv +// spell-checker:ignore (ToDO) filetime strptime utcoff strs datetime MMDDhhmm clapv PWSTR lpszfilepath hresult pub extern crate filetime; @@ -16,7 +16,7 @@ extern crate uucore; use clap::{crate_version, App, AppSettings, Arg, ArgGroup}; use filetime::*; use std::fs::{self, File}; -use std::path::Path; +use std::path::{Path, PathBuf}; use uucore::display::Quotable; use uucore::error::{FromIo, UError, UResult, USimpleError}; use uucore::format_usage; @@ -77,7 +77,15 @@ Try 'touch --help' for more information."##, }; for filename in files { - let path = Path::new(filename); + // FIXME: find a way to avoid having to clone the path + let pathbuf = if filename == "-" { + pathbuf_from_stdout()? + } else { + PathBuf::from(filename) + }; + + let path = pathbuf.as_path(); + if !path.exists() { if matches.is_present(options::NO_CREATE) { continue; @@ -299,3 +307,89 @@ fn parse_timestamp(s: &str) -> UResult { Ok(ft) } + +// TODO: this may be a good candidate to put in fsext.rs +/// Returns a PathBuf to stdout. +/// +/// On Windows, uses GetFinalPathNameByHandleW to attempt to get the path +/// from the stdout handle. +fn pathbuf_from_stdout() -> UResult { + #[cfg(unix)] + { + Ok(PathBuf::from("/dev/stdout")) + } + #[cfg(windows)] + { + use std::os::windows::prelude::AsRawHandle; + use winapi::shared::minwindef::{DWORD, MAX_PATH}; + use winapi::shared::winerror::{ + ERROR_INVALID_PARAMETER, ERROR_NOT_ENOUGH_MEMORY, ERROR_PATH_NOT_FOUND, + }; + use winapi::um::errhandlingapi::GetLastError; + use winapi::um::fileapi::GetFinalPathNameByHandleW; + use winapi::um::winnt::WCHAR; + + let handle = std::io::stdout().lock().as_raw_handle(); + let mut file_path_buffer: [WCHAR; MAX_PATH as usize] = [0; MAX_PATH as usize]; + + // Couldn't find this in winapi + const FILE_NAME_OPENED: DWORD = 0x8; + + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea#examples + // SAFETY: We transmute the handle to be able to cast *mut c_void into a + // HANDLE (i32) so rustc will let us call GetFinalPathNameByHandleW. The + // reference example code for GetFinalPathNameByHandleW implies that + // it is safe for us to leave lpszfilepath uninitialized, so long as + // the buffer size is correct. We know the buffer size (MAX_PATH) at + // compile time. MAX_PATH is a small number (260) so we can cast it + // to a u32. + let ret = unsafe { + GetFinalPathNameByHandleW( + std::mem::transmute(handle), + file_path_buffer.as_mut_ptr(), + file_path_buffer.len() as u32, + FILE_NAME_OPENED, + ) + }; + + let buffer_size = match ret { + ERROR_PATH_NOT_FOUND | ERROR_NOT_ENOUGH_MEMORY | ERROR_INVALID_PARAMETER => { + return Err(USimpleError::new( + 1, + format!("GetFinalPathNameByHandleW failed with code {}", ret), + )) + } + e if e == 0 => { + return Err(USimpleError::new( + 1, + format!( + "GetFinalPathNameByHandleW failed with code {}", + // SAFETY: GetLastError is thread-safe and has no documented memory unsafety. + unsafe { GetLastError() } + ), + )); + } + e => e as usize, + }; + + // Don't include the null terminator + Ok(String::from_utf16(&file_path_buffer[0..buffer_size]) + .map_err(|e| USimpleError::new(1, e.to_string()))? + .into()) + } +} + +#[cfg(test)] +mod tests { + #[cfg(windows)] + #[test] + fn test_get_pathbuf_from_stdout_fails_if_stdout_is_not_a_file() { + // We can trigger an error by not setting stdout to anything (will + // fail with code 1) + assert!(super::pathbuf_from_stdout() + .err() + .expect("pathbuf_from_stdout should have failed") + .to_string() + .contains("GetFinalPathNameByHandleW failed with code 1")); + } +} diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index ac82a81ea..f869d1560 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -510,6 +510,27 @@ fn test_touch_no_such_file_error_msg() { )); } +#[test] +fn test_touch_changes_time_of_file_in_stdout() { + // command like: `touch - 1< ./c` + // should change the timestamp of c + + let (at, mut ucmd) = at_and_ucmd!(); + let file = "test_touch_changes_time_of_file_in_stdout"; + + at.touch(file); + assert!(at.file_exists(file)); + let (_, mtime) = get_file_times(&at, file); + + ucmd.args(&["-"]) + .set_stdout(at.make_file(file)) + .succeeds() + .no_stderr(); + + let (_, mtime_after) = get_file_times(&at, file); + assert!(mtime_after != mtime); +} + #[test] #[cfg(unix)] fn test_touch_permission_denied_error_msg() { From dfb794d37c1e5a5ee289c7e08f03b6cc15711f38 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 6 Mar 2022 23:32:12 +0100 Subject: [PATCH 24/60] Run dependabot to update deps --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..baf9fc610 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 5 From b85cc9058649d73ca946d89232c3ff5f26f7266e Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 6 Mar 2022 23:33:22 +0100 Subject: [PATCH 25/60] Fix yaml syntax --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index baf9fc610..ef7519b88 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,4 +4,4 @@ updates: directory: "/" schedule: interval: "daily" - open-pull-requests-limit: 5 + open-pull-requests-limit: 5 From 2e50eade7f79267ea57c5279c420c408eff82f93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Mar 2022 22:35:33 +0000 Subject: [PATCH 26/60] build(deps): bump once_cell from 1.9.0 to 1.10.0 Bumps [once_cell](https://github.com/matklad/once_cell) from 1.9.0 to 1.10.0. - [Release notes](https://github.com/matklad/once_cell/releases) - [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md) - [Commits](https://github.com/matklad/once_cell/compare/v1.9.0...v1.10.0) --- updated-dependencies: - dependency-name: once_cell dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- src/uu/ls/Cargo.toml | 2 +- src/uucore/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f7207a62..51896a258 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1319,9 +1319,9 @@ checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" [[package]] name = "once_cell" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "onig" diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index 22a5a1aed..6ad91e8e7 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -24,7 +24,7 @@ termsize = "0.1.6" glob = "0.3.0" lscolors = { version = "0.7.1", features = ["ansi_term"] } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] } -once_cell = "1.7.2" +once_cell = "1.10.0" atty = "0.2" selinux = { version="0.2", optional = true } diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index a8d992fbb..c62e25d0b 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -30,7 +30,7 @@ data-encoding = { version="2.1", optional=true } data-encoding-macro = { version="0.1.12", optional=true } z85 = { version="3.0.3", optional=true } libc = { version="0.2.15", optional=true } -once_cell = "1.8.0" +once_cell = "1.10.0" os_display = "0.1.0" [target.'cfg(unix)'.dependencies] From 86884131b7e89d7a53afc80a39514b91bb556428 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Mar 2022 22:35:47 +0000 Subject: [PATCH 27/60] build(deps): bump memmap2 from 0.5.2 to 0.5.3 Bumps [memmap2](https://github.com/RazrFalcon/memmap2-rs) from 0.5.2 to 0.5.3. - [Release notes](https://github.com/RazrFalcon/memmap2-rs/releases) - [Changelog](https://github.com/RazrFalcon/memmap2-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/RazrFalcon/memmap2-rs/compare/v0.5.2...v0.5.3) --- updated-dependencies: - dependency-name: memmap2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f7207a62..c69a7dcd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1144,9 +1144,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "memmap2" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3179b85e1fd8b14447cbebadb75e45a1002f541b925f0bfec366d56a81c56d" +checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" dependencies = [ "libc", ] From 1c469ec6c5bbb8d73aea85d37cb4d637318c56fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Mar 2022 22:36:00 +0000 Subject: [PATCH 28/60] build(deps): bump ouroboros from 0.10.1 to 0.14.2 Bumps [ouroboros](https://github.com/joshua-maros/ouroboros) from 0.10.1 to 0.14.2. - [Release notes](https://github.com/joshua-maros/ouroboros/releases) - [Commits](https://github.com/joshua-maros/ouroboros/commits/0.14.2) --- updated-dependencies: - dependency-name: ouroboros dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- src/uu/sort/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f7207a62..e94ba57ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1375,9 +1375,9 @@ dependencies = [ [[package]] name = "ouroboros" -version = "0.10.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84236d64f1718c387232287cf036eb6632a5ecff226f4ff9dccb8c2b79ba0bde" +checksum = "71643f290d126e18ac2598876d01e1d57aed164afc78fdb6e2a0c6589a1f6662" dependencies = [ "aliasable", "ouroboros_macro", @@ -1386,9 +1386,9 @@ dependencies = [ [[package]] name = "ouroboros_macro" -version = "0.10.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f463857a6eb96c0136b1d56e56c718350cef30412ec065b48294799a088bca68" +checksum = "ed9a247206016d424fe8497bc611e510887af5c261fbbf977877c4bb55ca4d82" dependencies = [ "Inflector", "proc-macro-error", diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index bd74bce85..12b171b74 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -22,7 +22,7 @@ ctrlc = { version = "3.0", features = ["termination"] } fnv = "1.0.7" itertools = "0.10.0" memchr = "2.4.0" -ouroboros = "0.10.1" +ouroboros = "0.14.2" rand = "0.8" rayon = "1.5" tempfile = "3" From 31c7f0963093b79d11e40bd328abdc487e96f6a0 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sun, 6 Mar 2022 22:36:26 -0500 Subject: [PATCH 29/60] util: remove some workarounds for GNU tests --- util/build-gnu.sh | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index bb8d6e522..8e6dd0bb1 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -118,22 +118,17 @@ sed -i -e '/tests\/misc\/seq-precision.sh/ D' \ sed -i '/INT_OFLOW/ D' tests/misc/printf.sh # Use the system coreutils where the test fails due to error in a util that is not the one being tested -sed -i 's|stat|/usr/bin/stat|' tests/chgrp/basic.sh tests/cp/existing-perm-dir.sh tests/touch/60-seconds.sh tests/misc/sort-compress-proc.sh -sed -i 's|ls -|/usr/bin/ls -|' tests/chgrp/posix-H.sh tests/chown/deref.sh tests/cp/same-file.sh tests/misc/mknod.sh tests/mv/part-symlink.sh tests/du/8gb.sh -sed -i 's|mkdir |/usr/bin/mkdir |' tests/cp/existing-perm-dir.sh tests/rm/empty-inacc.sh +sed -i 's|stat|/usr/bin/stat|' tests/touch/60-seconds.sh tests/misc/sort-compress-proc.sh +sed -i 's|ls -|/usr/bin/ls -|' tests/cp/same-file.sh tests/misc/mknod.sh tests/mv/part-symlink.sh sed -i 's|timeout \([[:digit:]]\)| /usr/bin/timeout \1|' tests/tail-2/inotify-rotate.sh tests/tail-2/inotify-dir-recreate.sh tests/tail-2/inotify-rotate-resources.sh tests/cp/parent-perm-race.sh tests/ls/infloop.sh tests/misc/sort-exit-early.sh tests/misc/sort-NaN-infloop.sh tests/misc/uniq-perf.sh tests/tail-2/inotify-only-regular.sh tests/tail-2/pipe-f2.sh tests/tail-2/retry.sh tests/tail-2/symlink.sh tests/tail-2/wait.sh tests/tail-2/pid.sh tests/dd/stats.sh tests/tail-2/follow-name.sh tests/misc/shuf.sh # Don't break the function called 'grep_timeout' -sed -i 's|chmod |/usr/bin/chmod |' tests/du/inacc-dir.sh tests/mkdir/p-3.sh tests/tail-2/tail-n0f.sh tests/cp/fail-perm.sh tests/du/inaccessible-cwd.sh tests/mv/i-2.sh tests/chgrp/basic.sh tests/misc/shuf.sh +sed -i 's|chmod |/usr/bin/chmod |' tests/du/inacc-dir.sh tests/tail-2/tail-n0f.sh tests/cp/fail-perm.sh tests/mv/i-2.sh tests/misc/shuf.sh sed -i 's|sort |/usr/bin/sort |' tests/ls/hyperlink.sh tests/misc/test-N.sh sed -i 's|split |/usr/bin/split |' tests/misc/factor-parallel.sh -sed -i 's|truncate |/usr/bin/truncate |' tests/split/fail.sh -sed -i 's|dd |/usr/bin/dd |' tests/du/8gb.sh tests/tail-2/big-4gb.sh init.cfg sed -i 's|id -|/usr/bin/id -|' tests/misc/runcon-no-reorder.sh -sed -i 's|touch |/usr/bin/touch |' tests/cp/preserve-link.sh tests/cp/reflink-perm.sh tests/ls/block-size.sh tests/ls/abmon-align.sh tests/ls/rt-1.sh tests/mv/update.sh tests/misc/ls-time.sh tests/misc/stat-nanoseconds.sh tests/misc/time-style.sh tests/misc/test-N.sh +sed -i 's|touch |/usr/bin/touch |' tests/cp/preserve-link.sh tests/cp/reflink-perm.sh tests/ls/block-size.sh tests/mv/update.sh tests/misc/ls-time.sh tests/misc/stat-nanoseconds.sh tests/misc/time-style.sh tests/misc/test-N.sh sed -i 's|ln -|/usr/bin/ln -|' tests/cp/link-deref.sh -sed -i 's|printf |/usr/bin/printf |g' tests/dd/ascii.sh sed -i 's|cp |/usr/bin/cp |' tests/mv/hard-2.sh sed -i 's|paste |/usr/bin/paste |' tests/misc/od-endian.sh -sed -i 's|seq |/usr/bin/seq |' tests/misc/sort-discrim.sh # Add specific timeout to tests that currently hang to limit time spent waiting sed -i 's|\(^\s*\)seq \$|\1/usr/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh From a74ffac19e254f2bf235ee2779940661d0667db6 Mon Sep 17 00:00:00 2001 From: Nick Donald Date: Fri, 11 Feb 2022 18:49:49 -0500 Subject: [PATCH 30/60] printf: fix printf sci notation round up --- .../num_format/formatters/float_common.rs | 49 ++++++++++++++----- tests/by-util/test_printf.rs | 24 +++++++++ 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/uucore/src/lib/features/tokenize/num_format/formatters/float_common.rs b/src/uucore/src/lib/features/tokenize/num_format/formatters/float_common.rs index e62245534..eed543807 100644 --- a/src/uucore/src/lib/features/tokenize/num_format/formatters/float_common.rs +++ b/src/uucore/src/lib/features/tokenize/num_format/formatters/float_common.rs @@ -153,7 +153,9 @@ fn de_hex(src: &str, before_decimal: bool) -> String { // bumps the last digit up one, // and if the digit was nine // propagate to the next, etc. -fn _round_str_from(in_str: &str, position: usize) -> (String, bool) { +// If before the decimal and the most +// significant digit is a 9, it becomes a 1 +fn _round_str_from(in_str: &str, position: usize, before_dec: bool) -> (String, bool) { let mut it = in_str[0..position].chars(); let mut rev = String::new(); let mut i = position; @@ -162,7 +164,14 @@ fn _round_str_from(in_str: &str, position: usize) -> (String, bool) { i -= 1; match c { '9' => { - rev.push('0'); + // If we're before the decimal + // and on the most significant digit, + // round 9 to 1, else to 0. + if before_dec && i == 0 { + rev.push('1'); + } else { + rev.push('0'); + } } e => { rev.push(((e as u8) + 1) as char); @@ -182,24 +191,32 @@ fn round_terminal_digit( before_dec: String, after_dec: String, position: usize, -) -> (String, String) { +) -> (String, String, bool) { if position < after_dec.len() { let digit_at_pos: char; { digit_at_pos = after_dec[position..=position].chars().next().expect(""); } if let '5'..='9' = digit_at_pos { - let (new_after_dec, finished_in_dec) = _round_str_from(&after_dec, position); + let (new_after_dec, finished_in_dec) = _round_str_from(&after_dec, position, false); if finished_in_dec { - return (before_dec, new_after_dec); + return (before_dec, new_after_dec, false); } else { - let (new_before_dec, _) = _round_str_from(&before_dec, before_dec.len()); - return (new_before_dec, new_after_dec); + let (new_before_dec, _) = _round_str_from(&before_dec, before_dec.len(), true); + let mut dec_place_chg = false; + let mut before_dec_chars = new_before_dec.chars(); + if before_dec_chars.next() == Some('1') && before_dec_chars.all(|c| c == '0') { + // If the first digit is a one and remaining are zeros, we have + // rounded to a new decimal place, so the decimal place must be updated. + // Only update decimal place if the before decimal != 0 + dec_place_chg = before_dec != "0"; + } + return (new_before_dec, new_after_dec, dec_place_chg); } // TODO } } - (before_dec, after_dec) + (before_dec, after_dec, false) } pub fn get_primitive_dec( @@ -237,7 +254,7 @@ pub fn get_primitive_dec( String::from(second_segment_raw), ), }; - let (pre_dec_unrounded, post_dec_unrounded, mantissa) = if sci_mode.is_some() { + let (pre_dec_unrounded, post_dec_unrounded, mut mantissa) = if sci_mode.is_some() { if first_segment.len() > 1 { let mut post_dec = String::from(&first_segment[1..]); post_dec.push_str(&second_segment); @@ -277,13 +294,15 @@ pub fn get_primitive_dec( (first_segment, second_segment, 0) }; - let (pre_dec_draft, post_dec_draft) = + let (pre_dec_draft, post_dec_draft, dec_place_chg) = round_terminal_digit(pre_dec_unrounded, post_dec_unrounded, last_dec_place - 1); - - f.pre_decimal = Some(pre_dec_draft); f.post_decimal = Some(post_dec_draft); if let Some(capitalized) = sci_mode { let si_ind = if capitalized { 'E' } else { 'e' }; + // Increase the mantissa if we're adding a decimal place + if dec_place_chg { + mantissa += 1; + } f.suffix = Some(if mantissa >= 0 { format!("{}+{:02}", si_ind, mantissa) } else { @@ -291,6 +310,12 @@ pub fn get_primitive_dec( // leading zeroes format!("{}{:03}", si_ind, mantissa) }); + f.pre_decimal = Some(pre_dec_draft); + } else if dec_place_chg { + // We've rounded up to a new decimal place so append 0 + f.pre_decimal = Some(pre_dec_draft + "0"); + } else { + f.pre_decimal = Some(pre_dec_draft); } f diff --git a/tests/by-util/test_printf.rs b/tests/by-util/test_printf.rs index b3e608dc9..72ca4535a 100644 --- a/tests/by-util/test_printf.rs +++ b/tests/by-util/test_printf.rs @@ -228,6 +228,22 @@ fn sub_num_float() { .stdout_only("twenty is 20.000000"); } +#[test] +fn sub_num_float_e_round() { + new_ucmd!() + .args(&["%e", "99999999"]) + .succeeds() + .stdout_only("1.000000e+08"); +} + +#[test] +fn sub_num_float_e_no_round() { + new_ucmd!() + .args(&["%e", "99999994"]) + .succeeds() + .stdout_only("9.999999e+07"); +} + #[test] fn sub_num_float_round() { new_ucmd!() @@ -236,6 +252,14 @@ fn sub_num_float_round() { .stdout_only("two is 2.000000"); } +#[test] +fn sub_num_float_round_nines_dec() { + new_ucmd!() + .args(&["%f", "0.99999999"]) + .succeeds() + .stdout_only("1.000000"); +} + #[test] fn sub_num_sci_lower() { new_ucmd!() From 3317fb99244cccb126b8e60b0b12456a1ea57c8c Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 8 Mar 2022 15:13:05 +0100 Subject: [PATCH 31/60] df: always round up usage percentage (#3208) --- src/uu/df/src/table.rs | 32 +++++++++++++++++++++++++++++++- tests/by-util/test_df.rs | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 6d4a3bdd1..65d390dd2 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -222,7 +222,7 @@ impl<'a> DisplayRow<'a> { fn percentage(fraction: Option) -> String { match fraction { None => "-".to_string(), - Some(x) => format!("{:.0}%", 100.0 * x), + Some(x) => format!("{:.0}%", (100.0 * x).ceil()), } } @@ -551,4 +551,34 @@ mod tests { "my_device my_type 4.0Ki 1.0Ki 3.0Ki 25% my_mount " ); } + + #[test] + fn test_row_display_round_up_usage() { + let options = Options { + block_size: BlockSize::Bytes(1), + ..Default::default() + }; + let row = Row { + fs_device: "my_device".to_string(), + fs_type: "my_type".to_string(), + fs_mount: "my_mount".to_string(), + + bytes: 100, + bytes_used: 25, + bytes_free: 75, + bytes_usage: Some(0.251), + + #[cfg(target_os = "macos")] + bytes_capacity: Some(0.5), + + inodes: 10, + inodes_used: 2, + inodes_free: 8, + inodes_usage: Some(0.2), + }; + assert_eq!( + DisplayRow::new(row, &options).to_string(), + "my_device 100 25 75 26% my_mount " + ); + } } diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 455c75c7c..8f8474901 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -120,7 +120,40 @@ fn test_total() { assert_eq!(computed_total_size, reported_total_size); assert_eq!(computed_total_used, reported_total_used); assert_eq!(computed_total_avail, reported_total_avail); - // TODO We could also check here that the use percentage matches. +} + +#[test] +fn test_use_percentage() { + // Example output: + // + // Filesystem 1K-blocks Used Available Use% Mounted on + // udev 3858016 0 3858016 0% /dev + // ... + // /dev/loop14 63488 63488 0 100% /snap/core20/1361 + let output = new_ucmd!().succeeds().stdout_move_str(); + + // Skip the header line. + let lines: Vec<&str> = output.lines().skip(1).collect(); + + for line in lines { + let mut iter = line.split_whitespace(); + iter.next(); + let reported_size = iter.next().unwrap().parse::().unwrap(); + let reported_used = iter.next().unwrap().parse::().unwrap(); + // Skip "Available" column + iter.next(); + if cfg!(target_os = "macos") { + // Skip "Capacity" column + iter.next(); + } + let reported_percentage = iter.next().unwrap(); + let reported_percentage = reported_percentage[..reported_percentage.len() - 1] + .parse::() + .unwrap(); + let computed_percentage = (100.0 * (reported_used / reported_size)).ceil() as u8; + + assert_eq!(computed_percentage, reported_percentage); + } } #[test] From f896834c77d1bb3238fe29287607072903da6430 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 7 Mar 2022 21:25:58 +0100 Subject: [PATCH 32/60] Use min rust version as default --- .github/workflows/CICD.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index dd83c66df..2c4a428eb 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -636,7 +636,7 @@ jobs: # # https://github.com/rust-lang/rustup/issues/2229#issuecomment-585855925 # RUSTUP_UNPACK_RAM: "21474836480" with: - toolchain: ${{ steps.vars.outputs.TOOLCHAIN }} + toolchain: ${{ env.RUST_MIN_SRV }} target: ${{ matrix.job.target }} default: true profile: minimal # minimal component installation (ie, no documentation) @@ -688,18 +688,21 @@ jobs: use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }} command: build args: --release --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} + toolchain: ${{ env.RUST_MIN_SRV }} - name: Test uses: actions-rs/cargo@v1 with: use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }} command: test args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} + toolchain: ${{ env.RUST_MIN_SRV }} - name: Test individual utilities uses: actions-rs/cargo@v1 with: use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }} command: test args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} + toolchain: ${{ env.RUST_MIN_SRV }} - name: Archive executable artifacts uses: actions/upload-artifact@v2 with: From e3649e33c51bed00121ae24afa783e3c59f23083 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 7 Mar 2022 21:43:42 +0100 Subject: [PATCH 33/60] remove old comment --- .github/workflows/CICD.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 2c4a428eb..f9273f6c9 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -631,10 +631,6 @@ jobs: esac - name: rust toolchain ~ install uses: actions-rs/toolchain@v1 - # env: - # # Override auto-detection of RAM for Rustc install. - # # https://github.com/rust-lang/rustup/issues/2229#issuecomment-585855925 - # RUSTUP_UNPACK_RAM: "21474836480" with: toolchain: ${{ env.RUST_MIN_SRV }} target: ${{ matrix.job.target }} From 2a48234c12497e9095f36082d855c42cb9e15064 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 7 Mar 2022 21:48:15 +0100 Subject: [PATCH 34/60] add a job to build with rust stable --- .github/workflows/CICD.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index f9273f6c9..6c98ae1ac 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -433,6 +433,32 @@ jobs: make test + build_rust_stable: + name: Build/stable + needs: [ min_version, deps ] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { os: ubuntu-latest , features: feat_os_unix } + - { os: macos-latest , features: feat_os_macos } + - { os: windows-latest , features: feat_os_windows } + steps: + - uses: actions/checkout@v2 + - uses: Swatinem/rust-cache@v1 + - name: Install `rust` toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + profile: minimal # minimal component installation (ie, no documentation) + - name: Test + uses: actions-rs/cargo@v1 + with: + command: test + args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} + compute_size: name: Binary sizes needs: [ min_version, deps ] From 9f5fad1e3847c0bead91fcdbc8ca6636a95fe935 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 7 Mar 2022 21:48:42 +0100 Subject: [PATCH 35/60] add a job to build with rust nightly --- .github/workflows/CICD.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 6c98ae1ac..361a8c48b 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -459,6 +459,32 @@ jobs: command: test args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} + build_rust_nightly: + name: Build/nightly + needs: [ min_version, deps ] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { os: ubuntu-latest , features: feat_os_unix } + - { os: macos-latest , features: feat_os_macos } + - { os: windows-latest , features: feat_os_windows } + steps: + - uses: actions/checkout@v2 + - uses: Swatinem/rust-cache@v1 + - name: Install `rust` toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + default: true + profile: minimal # minimal component installation (ie, no documentation) + - name: Test + uses: actions-rs/cargo@v1 + with: + command: test + args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} + compute_size: name: Binary sizes needs: [ min_version, deps ] From a22709aedd8478d58169db565bc746b70fe19236 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 8 Mar 2022 22:07:16 +0100 Subject: [PATCH 36/60] CI: Disable Windows job i686-pc-windows-gnu Fails with: error: linker `i686-w64-mingw32-gcc` not found And the Windows GNU is covered by x86_64-pc-windows-gnu anyway --- .github/workflows/CICD.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 361a8c48b..ecb1d8d6f 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -557,7 +557,6 @@ jobs: #- { os: ubuntu-18.04 , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } #- { os: ubuntu-18.04 , target: x86_64-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross } - { os: macos-latest , target: x86_64-apple-darwin , features: feat_os_macos } - - { os: windows-latest , target: i686-pc-windows-gnu , features: feat_os_windows } - { os: windows-latest , target: i686-pc-windows-msvc , features: feat_os_windows } - { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly - { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows } From bd04e0f92f6c73adc8694046e942ddac73f7e46b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Mar 2022 06:35:39 +0000 Subject: [PATCH 37/60] build(deps): bump unindent from 0.1.7 to 0.1.8 Bumps [unindent](https://github.com/dtolnay/indoc) from 0.1.7 to 0.1.8. - [Release notes](https://github.com/dtolnay/indoc/releases) - [Commits](https://github.com/dtolnay/indoc/compare/0.1.7...0.1.8) --- updated-dependencies: - dependency-name: unindent dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12c501476..e75d9227d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2232,9 +2232,9 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "unindent" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" +checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8" [[package]] name = "unix_socket" From b4ee7f84fac605106c6339a019cc963c1c17eb64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Mar 2022 22:35:19 +0000 Subject: [PATCH 38/60] build(deps): bump unicode-segmentation from 1.8.0 to 1.9.0 Bumps [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation) from 1.8.0 to 1.9.0. - [Release notes](https://github.com/unicode-rs/unicode-segmentation/releases) - [Commits](https://github.com/unicode-rs/unicode-segmentation/commits) --- updated-dependencies: - dependency-name: unicode-segmentation dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- src/uu/more/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12c501476..740ed8dde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2208,9 +2208,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "unicode-width" diff --git a/src/uu/more/Cargo.toml b/src/uu/more/Cargo.toml index d8e48cbd7..6975a3ecc 100644 --- a/src/uu/more/Cargo.toml +++ b/src/uu/more/Cargo.toml @@ -20,7 +20,7 @@ uucore = { version = ">=0.0.7", package = "uucore", path = "../../uucore" } crossterm = ">=0.19" atty = "0.2" unicode-width = "0.1.7" -unicode-segmentation = "1.7.1" +unicode-segmentation = "1.9.0" [target.'cfg(target_os = "redox")'.dependencies] redox_termios = "0.1" From fc4f1d869ea3bd4ab9cbc7facb5e57bee371d51c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Mar 2022 22:35:06 +0000 Subject: [PATCH 39/60] build(deps): bump strum from 0.23.0 to 0.24.0 Bumps [strum](https://github.com/Peternator7/strum) from 0.23.0 to 0.24.0. - [Release notes](https://github.com/Peternator7/strum/releases) - [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md) - [Commits](https://github.com/Peternator7/strum/commits) --- updated-dependencies: - dependency-name: strum dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- src/uu/uniq/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12c501476..4d8fcaf32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2000,9 +2000,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb" +checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8" [[package]] name = "strum_macros" diff --git a/src/uu/uniq/Cargo.toml b/src/uu/uniq/Cargo.toml index a6c9810b9..1fe571b0e 100644 --- a/src/uu/uniq/Cargo.toml +++ b/src/uu/uniq/Cargo.toml @@ -16,7 +16,7 @@ path = "src/uniq.rs" [dependencies] clap = { version = "3.0", features = ["wrap_help", "cargo"] } -strum = "0.23.0" +strum = "0.24.0" strum_macros = "0.23.1" uucore = { version=">=0.0.11", package="uucore", path="../../uucore" } From 3955354b3a9283b0f1217887e7a23dd1d35f7575 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 6 Mar 2022 23:17:29 +0100 Subject: [PATCH 40/60] timeout: add a test for empty arg --- tests/by-util/test_timeout.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/by-util/test_timeout.rs b/tests/by-util/test_timeout.rs index 9be29065a..5db91a44d 100644 --- a/tests/by-util/test_timeout.rs +++ b/tests/by-util/test_timeout.rs @@ -45,3 +45,11 @@ fn test_zero_timeout() { .no_stderr() .no_stdout(); } + +#[test] +fn test_command_empty_args() { + new_ucmd!() + .args(&["", ""]) + .fails() + .stderr_contains("timeout: empty string"); +} From a5200efaacbf805a736fd692483f2a83d53c567b Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Thu, 10 Mar 2022 18:50:37 -0500 Subject: [PATCH 41/60] util: remove replacement of seq in GNU tests --- util/build-gnu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 8e6dd0bb1..f2352f7c0 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -85,7 +85,7 @@ if test ${UU_MAKE_PROFILE} != "debug"; then for i in ${seq}; do make "tests/factor/t${i}.sh" done - sed -i -e 's|^seq |/usr/bin/seq |' -e 's|sha1sum |/usr/bin/sha1sum |' tests/factor/t*sh + sed -i -e 's|sha1sum |/usr/bin/sha1sum |' tests/factor/t*sh first=21 fi # strip all (debug) or just the longer (release) factor tests from Makefile From b42168e9dc1f90c641653b3a358bd2e07d4b745a Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Thu, 10 Mar 2022 22:31:21 -0500 Subject: [PATCH 42/60] Clippy fixes in multiple crates --- src/uu/dd/src/parseargs.rs | 7 ++++--- src/uu/df/src/table.rs | 2 +- src/uu/split/src/number.rs | 2 +- tests/by-util/test_sort.rs | 38 ++++++++++++++++---------------------- tests/by-util/test_who.rs | 6 ++---- 5 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 081055cf9..d8639fca9 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -807,15 +807,16 @@ mod tests { #[test] fn test_parse_bytes_with_opt_multiplier() { assert_eq!(parse_bytes_with_opt_multiplier("123").unwrap(), 123); - assert_eq!(parse_bytes_with_opt_multiplier("123c").unwrap(), 123 * 1); + assert_eq!(parse_bytes_with_opt_multiplier("123c").unwrap(), 123); // 123 * 1 assert_eq!(parse_bytes_with_opt_multiplier("123w").unwrap(), 123 * 2); assert_eq!(parse_bytes_with_opt_multiplier("123b").unwrap(), 123 * 512); assert_eq!(parse_bytes_with_opt_multiplier("123x3").unwrap(), 123 * 3); assert_eq!(parse_bytes_with_opt_multiplier("123k").unwrap(), 123 * 1024); - assert_eq!(parse_bytes_with_opt_multiplier("1x2x3").unwrap(), 1 * 2 * 3); + assert_eq!(parse_bytes_with_opt_multiplier("1x2x3").unwrap(), 6); // 1 * 2 * 3 + assert_eq!( parse_bytes_with_opt_multiplier("1wx2cx3w").unwrap(), - (1 * 2) * (2 * 1) * (3 * 2) + 2 * 2 * (3 * 2) // (1 * 2) * (2 * 1) * (3 * 2) ); assert!(parse_bytes_with_opt_multiplier("123asdf").is_err()); } diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 65d390dd2..c8ac4989e 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -577,7 +577,7 @@ mod tests { inodes_usage: Some(0.2), }; assert_eq!( - DisplayRow::new(row, &options).to_string(), + DisplayRow::new(&row, &options).to_string(), "my_device 100 25 75 26% my_mount " ); } diff --git a/src/uu/split/src/number.rs b/src/uu/split/src/number.rs index d5427e2ca..c7557271d 100644 --- a/src/uu/split/src/number.rs +++ b/src/uu/split/src/number.rs @@ -479,7 +479,7 @@ mod tests { fn num(n: usize) -> Number { let mut number = Number::DynamicWidth(DynamicWidthNumber::new(16)); for _ in 0..n { - number.increment().unwrap() + number.increment().unwrap(); } number } diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 5ae56b29e..c33d1a986 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -338,24 +338,20 @@ fn test_dictionary_order() { #[test] fn test_dictionary_order2() { - for non_dictionary_order2_param in &["-d"] { - new_ucmd!() - .pipe_in("a👦🏻aa\tb\naaaa\tb") // spell-checker:disable-line - .arg(non_dictionary_order2_param) // spell-checker:disable-line - .succeeds() - .stdout_only("a👦🏻aa\tb\naaaa\tb\n"); // spell-checker:disable-line - } + new_ucmd!() + .pipe_in("a👦🏻aa\tb\naaaa\tb") // spell-checker:disable-line + .arg("-d") + .succeeds() + .stdout_only("a👦🏻aa\tb\naaaa\tb\n"); // spell-checker:disable-line } #[test] fn test_non_printing_chars() { - for non_printing_chars_param in &["-i"] { - new_ucmd!() - .pipe_in("a👦🏻aa\naaaa") // spell-checker:disable-line - .arg(non_printing_chars_param) // spell-checker:disable-line - .succeeds() - .stdout_only("a👦🏻aa\naaaa\n"); // spell-checker:disable-line - } + new_ucmd!() + .pipe_in("a👦🏻aa\naaaa") // spell-checker:disable-line + .arg("-i") + .succeeds() + .stdout_only("a👦🏻aa\naaaa\n"); // spell-checker:disable-line } #[test] @@ -486,14 +482,12 @@ fn test_default_unsorted_ints2() { #[test] fn test_numeric_unique_ints2() { - for numeric_unique_sort_param in &["-nu"] { - let input = "9\n9\n8\n1\n"; - new_ucmd!() - .arg(numeric_unique_sort_param) - .pipe_in(input) - .succeeds() - .stdout_only("1\n8\n9\n"); - } + let input = "9\n9\n8\n1\n"; + new_ucmd!() + .arg("-nu") + .pipe_in(input) + .succeeds() + .stdout_only("1\n8\n9\n"); } #[test] diff --git a/tests/by-util/test_who.rs b/tests/by-util/test_who.rs index 8c3bba25c..2687539f1 100644 --- a/tests/by-util/test_who.rs +++ b/tests/by-util/test_who.rs @@ -69,10 +69,8 @@ fn test_login() { #[test] fn test_m() { let ts = TestScenario::new(util_name!()); - for opt in &["-m"] { - let expected_stdout = unwrap_or_return!(expected_result(&ts, &[opt])).stdout_move_str(); - ts.ucmd().arg(opt).succeeds().stdout_is(expected_stdout); - } + let expected_stdout = unwrap_or_return!(expected_result(&ts, &["-m"])).stdout_move_str(); + ts.ucmd().arg("-m").succeeds().stdout_is(expected_stdout); } #[cfg(unix)] From 0b07ecdad228af02808ab6548a743a7c886d169f Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 21 Feb 2022 19:52:04 -0500 Subject: [PATCH 43/60] df: use Options.columns field to control output Replace the `Options.show_fs_type` and `Options.show_inode_instead` fields with the more general `Options.columns`, a `Vec` of `Column` variants representing the exact sequence of columns to display in the output. This makes `Options` able to represent arbitrary output column display configurations. --- src/uu/df/src/columns.rs | 109 ++++++++++++++++++++++++++++ src/uu/df/src/df.rs | 32 +++++++-- src/uu/df/src/table.rs | 149 ++++++++++++++++++++------------------- 3 files changed, 210 insertions(+), 80 deletions(-) create mode 100644 src/uu/df/src/columns.rs diff --git a/src/uu/df/src/columns.rs b/src/uu/df/src/columns.rs new file mode 100644 index 000000000..f9b4c4f2b --- /dev/null +++ b/src/uu/df/src/columns.rs @@ -0,0 +1,109 @@ +// * This file is part of the uutils coreutils package. +// * +// * For the full copyright and license information, please view the LICENSE +// * file that was distributed with this source code. +// spell-checker:ignore itotal iused iavail ipcent pcent squashfs +use crate::{OPT_INODES, OPT_PRINT_TYPE}; +use clap::ArgMatches; + +/// The columns in the output table produced by `df`. +/// +/// The [`Row`] struct has a field corresponding to each of the +/// variants of this enumeration. +/// +/// [`Row`]: crate::table::Row +#[derive(PartialEq, Copy, Clone)] +pub(crate) enum Column { + /// The source of the mount point, usually a device. + Source, + + /// Total number of blocks. + Size, + + /// Number of used blocks. + Used, + + /// Number of available blocks. + Avail, + + /// Percentage of blocks used out of total number of blocks. + Pcent, + + /// The mount point. + Target, + + /// Total number of inodes. + Itotal, + + /// Number of used inodes. + Iused, + + /// Number of available inodes. + Iavail, + + /// Percentage of inodes used out of total number of inodes. + Ipcent, + + /// The filename given as a command-line argument. + File, + + /// The filesystem type, like "ext4" or "squashfs". + Fstype, + + /// Percentage of bytes available to non-privileged processes. + #[cfg(target_os = "macos")] + Capacity, +} + +impl Column { + /// Convert from command-line arguments to sequence of columns. + /// + /// The set of columns that will appear in the output table can be + /// specified by command-line arguments. This function converts + /// those arguments to a [`Vec`] of [`Column`] variants. + pub(crate) fn from_matches(matches: &ArgMatches) -> Vec { + match ( + matches.is_present(OPT_PRINT_TYPE), + matches.is_present(OPT_INODES), + ) { + (false, false) => vec![ + Self::Source, + Self::Size, + Self::Used, + Self::Avail, + #[cfg(target_os = "macos")] + Self::Capacity, + Self::Pcent, + Self::Target, + ], + (false, true) => vec![ + Self::Source, + Self::Itotal, + Self::Iused, + Self::Iavail, + Self::Ipcent, + Self::Target, + ], + (true, false) => vec![ + Self::Source, + Self::Fstype, + Self::Size, + Self::Used, + Self::Avail, + #[cfg(target_os = "macos")] + Self::Capacity, + Self::Pcent, + Self::Target, + ], + (true, true) => vec![ + Self::Source, + Self::Fstype, + Self::Itotal, + Self::Iused, + Self::Iavail, + Self::Ipcent, + Self::Target, + ], + } + } +} diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 65091b4cc..62e669282 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -7,6 +7,7 @@ // that was distributed with this source code. // spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs mod blocks; +mod columns; mod table; use uucore::error::{UResult, USimpleError}; @@ -25,6 +26,7 @@ use std::iter::FromIterator; use std::path::Path; use crate::blocks::{block_size_from_matches, BlockSize}; +use crate::columns::Column; use crate::table::{DisplayRow, Header, Row}; static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\ @@ -66,18 +68,37 @@ struct FsSelector { /// Most of these parameters control which rows and which columns are /// displayed. The `block_size` determines the units to use when /// displaying numbers of bytes or inodes. -#[derive(Default)] struct Options { show_local_fs: bool, show_all_fs: bool, show_listed_fs: bool, - show_fs_type: bool, - show_inode_instead: bool, block_size: BlockSize, fs_selector: FsSelector, - /// Whether to show a final row comprising the totals for each column. show_total: bool, + /// Sequence of columns to display in the output table. + columns: Vec, +} + +impl Default for Options { + fn default() -> Self { + Self { + show_local_fs: Default::default(), + show_all_fs: Default::default(), + show_listed_fs: Default::default(), + block_size: Default::default(), + fs_selector: Default::default(), + show_total: Default::default(), + columns: vec![ + Column::Source, + Column::Size, + Column::Used, + Column::Avail, + Column::Pcent, + Column::Target, + ], + } + } } enum OptionsError { @@ -103,12 +124,11 @@ impl Options { show_local_fs: matches.is_present(OPT_LOCAL), show_all_fs: matches.is_present(OPT_ALL), show_listed_fs: false, - show_fs_type: matches.is_present(OPT_PRINT_TYPE), - show_inode_instead: matches.is_present(OPT_INODES), block_size: block_size_from_matches(matches) .map_err(|_| OptionsError::InvalidBlockSize)?, fs_selector: FsSelector::from(matches), show_total: matches.is_present(OPT_TOTAL), + columns: Column::from_matches(matches), }) } } diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 65d390dd2..a05b74d59 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -2,7 +2,7 @@ // * // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -// spell-checker:ignore tmpfs +// spell-checker:ignore tmpfs Pcent Itotal Iused Iavail Ipcent //! The filesystem usage data table. //! //! A table comprises a header row ([`Header`]) and a collection of @@ -11,6 +11,7 @@ //! [`DisplayRow`] implements [`std::fmt::Display`]. use number_prefix::NumberPrefix; +use crate::columns::Column; use crate::{BlockSize, Filesystem, Options}; use uucore::fsext::{FsUsage, MountInfo}; @@ -225,56 +226,37 @@ impl<'a> DisplayRow<'a> { Some(x) => format!("{:.0}%", (100.0 * x).ceil()), } } - - /// Write the bytes data for this row. - /// - /// # Errors - /// - /// If there is a problem writing to `f`. - /// - /// If the scaling factor is not 1000, 1024, or a negative number. - fn fmt_bytes(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{0: >12} ", self.scaled(self.row.bytes)?)?; - write!(f, "{0: >12} ", self.scaled(self.row.bytes_used)?)?; - write!(f, "{0: >12} ", self.scaled(self.row.bytes_free)?)?; - #[cfg(target_os = "macos")] - write!( - f, - "{0: >12} ", - DisplayRow::percentage(self.row.bytes_capacity) - )?; - write!(f, "{0: >5} ", DisplayRow::percentage(self.row.bytes_usage))?; - Ok(()) - } - - /// Write the inodes data for this row. - /// - /// # Errors - /// - /// If there is a problem writing to `f`. - /// - /// If the scaling factor is not 1000, 1024, or a negative number. - fn fmt_inodes(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{0: >12} ", self.scaled(self.row.inodes)?)?; - write!(f, "{0: >12} ", self.scaled(self.row.inodes_used)?)?; - write!(f, "{0: >12} ", self.scaled(self.row.inodes_free)?)?; - write!(f, "{0: >5} ", DisplayRow::percentage(self.row.inodes_usage))?; - Ok(()) - } } impl fmt::Display for DisplayRow<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{0: <16} ", self.row.fs_device)?; - if self.options.show_fs_type { - write!(f, "{0: <5} ", self.row.fs_type)?; + for column in &self.options.columns { + match column { + Column::Source => write!(f, "{0: <16} ", self.row.fs_device)?, + Column::Size => write!(f, "{0: >12} ", self.scaled(self.row.bytes)?)?, + Column::Used => write!(f, "{0: >12} ", self.scaled(self.row.bytes_used)?)?, + Column::Avail => write!(f, "{0: >12} ", self.scaled(self.row.bytes_free)?)?, + Column::Pcent => { + write!(f, "{0: >5} ", DisplayRow::percentage(self.row.bytes_usage))?; + } + Column::Target => write!(f, "{0: <16}", self.row.fs_mount)?, + Column::Itotal => write!(f, "{0: >12} ", self.scaled(self.row.inodes)?)?, + Column::Iused => write!(f, "{0: >12} ", self.scaled(self.row.inodes_used)?)?, + Column::Iavail => write!(f, "{0: >12} ", self.scaled(self.row.inodes_free)?)?, + Column::Ipcent => { + write!(f, "{0: >5} ", DisplayRow::percentage(self.row.inodes_usage))?; + } + // TODO Implement this. + Column::File => {} + Column::Fstype => write!(f, "{0: <5} ", self.row.fs_type)?, + #[cfg(target_os = "macos")] + Column::Capacity => write!( + f, + "{0: >12} ", + DisplayRow::percentage(self.row.bytes_capacity) + )?, + } } - if self.options.show_inode_instead { - self.fmt_inodes(f)?; - } else { - self.fmt_bytes(f)?; - } - write!(f, "{0: <16}", self.row.fs_mount)?; Ok(()) } } @@ -296,29 +278,29 @@ impl<'a> Header<'a> { impl fmt::Display for Header<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{0: <16} ", "Filesystem")?; - if self.options.show_fs_type { - write!(f, "{0: <5} ", "Type")?; + for column in &self.options.columns { + match column { + Column::Source => write!(f, "{0: <16} ", "Filesystem")?, + // `Display` is implemented for `BlockSize`, but + // `Display` only works when formatting an object into + // an empty format, `{}`. So we use `format!()` first + // to create the string, then use `write!()` to align + // the string and pad with spaces. + Column::Size => write!(f, "{0: >12} ", format!("{}", self.options.block_size))?, + Column::Used => write!(f, "{0: >12} ", "Used")?, + Column::Avail => write!(f, "{0: >12} ", "Available")?, + Column::Pcent => write!(f, "{0: >5} ", "Use%")?, + Column::Target => write!(f, "{0: <16} ", "Mounted on")?, + Column::Itotal => write!(f, "{0: >12} ", "Inodes")?, + Column::Iused => write!(f, "{0: >12} ", "IUsed")?, + Column::Iavail => write!(f, "{0: >12} ", "IFree")?, + Column::Ipcent => write!(f, "{0: >5} ", "IUse%")?, + Column::File => write!(f, "{0: <16}", "File")?, + Column::Fstype => write!(f, "{0: <5} ", "Type")?, + #[cfg(target_os = "macos")] + Column::Capacity => write!(f, "{0: >12} ", "Capacity")?, + } } - if self.options.show_inode_instead { - write!(f, "{0: >12} ", "Inodes")?; - write!(f, "{0: >12} ", "IUsed")?; - write!(f, "{0: >12} ", "IFree")?; - write!(f, "{0: >5} ", "IUse%")?; - } else { - // `Display` is implemented for `BlockSize`, but `Display` - // only works when formatting an object into an empty - // format, `{}`. So we use `format!()` first to create the - // string, then use `write!()` to align the string and pad - // with spaces. - write!(f, "{0: >12} ", format!("{}", self.options.block_size))?; - write!(f, "{0: >12} ", "Used")?; - write!(f, "{0: >12} ", "Available")?; - #[cfg(target_os = "macos")] - write!(f, "{0: >12} ", "Capacity")?; - write!(f, "{0: >5} ", "Use%")?; - } - write!(f, "{0: <16} ", "Mounted on")?; Ok(()) } } @@ -326,9 +308,28 @@ impl fmt::Display for Header<'_> { #[cfg(test)] mod tests { + use crate::columns::Column; use crate::table::{DisplayRow, Header, Row}; use crate::{BlockSize, Options}; + const COLUMNS_WITH_FS_TYPE: [Column; 7] = [ + Column::Source, + Column::Fstype, + Column::Size, + Column::Used, + Column::Avail, + Column::Pcent, + Column::Target, + ]; + const COLUMNS_WITH_INODES: [Column; 6] = [ + Column::Source, + Column::Itotal, + Column::Iused, + Column::Iavail, + Column::Ipcent, + Column::Target, + ]; + #[test] fn test_header_display() { let options = Default::default(); @@ -341,7 +342,7 @@ mod tests { #[test] fn test_header_display_fs_type() { let options = Options { - show_fs_type: true, + columns: COLUMNS_WITH_FS_TYPE.to_vec(), ..Default::default() }; assert_eq!( @@ -353,7 +354,7 @@ mod tests { #[test] fn test_header_display_inode() { let options = Options { - show_inode_instead: true, + columns: COLUMNS_WITH_INODES.to_vec(), ..Default::default() }; assert_eq!( @@ -431,8 +432,8 @@ mod tests { #[test] fn test_row_display_fs_type() { let options = Options { + columns: COLUMNS_WITH_FS_TYPE.to_vec(), block_size: BlockSize::Bytes(1), - show_fs_type: true, ..Default::default() }; let row = Row { @@ -462,8 +463,8 @@ mod tests { #[test] fn test_row_display_inodes() { let options = Options { + columns: COLUMNS_WITH_INODES.to_vec(), block_size: BlockSize::Bytes(1), - show_inode_instead: true, ..Default::default() }; let row = Row { @@ -494,7 +495,7 @@ mod tests { fn test_row_display_human_readable_si() { let options = Options { block_size: BlockSize::HumanReadableDecimal, - show_fs_type: true, + columns: COLUMNS_WITH_FS_TYPE.to_vec(), ..Default::default() }; let row = Row { @@ -525,7 +526,7 @@ mod tests { fn test_row_display_human_readable_binary() { let options = Options { block_size: BlockSize::HumanReadableBinary, - show_fs_type: true, + columns: COLUMNS_WITH_FS_TYPE.to_vec(), ..Default::default() }; let row = Row { From ec048b3857bddeca0e14c8ddb5e763191f8e1ac0 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 21 Feb 2022 19:54:12 -0500 Subject: [PATCH 44/60] df: implement the --output command-line argument Implement the `--output` command-line argument, which allows specifying an exact sequence of columns to display in the output table. For example, $ df --output=source,fstype | head -n3 Filesystem Type udev devtmpfs tmpfs tmpfs (The spacing does not exactly match the spacing of GNU `df` yet.) Fixes #3057. --- src/uu/df/src/columns.rs | 67 +++++++++++++++++++++++++++++++++++++--- tests/by-util/test_df.rs | 29 +++++++++++++++++ 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/src/uu/df/src/columns.rs b/src/uu/df/src/columns.rs index f9b4c4f2b..89dd35220 100644 --- a/src/uu/df/src/columns.rs +++ b/src/uu/df/src/columns.rs @@ -3,7 +3,7 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. // spell-checker:ignore itotal iused iavail ipcent pcent squashfs -use crate::{OPT_INODES, OPT_PRINT_TYPE}; +use crate::{OPT_INODES, OPT_OUTPUT, OPT_PRINT_TYPE}; use clap::ArgMatches; /// The columns in the output table produced by `df`. @@ -65,8 +65,9 @@ impl Column { match ( matches.is_present(OPT_PRINT_TYPE), matches.is_present(OPT_INODES), + matches.occurrences_of(OPT_OUTPUT) > 0, ) { - (false, false) => vec![ + (false, false, false) => vec![ Self::Source, Self::Size, Self::Used, @@ -76,7 +77,20 @@ impl Column { Self::Pcent, Self::Target, ], - (false, true) => vec![ + (false, false, true) => { + matches + .values_of(OPT_OUTPUT) + .unwrap() + .map(|s| { + // Unwrapping here should not panic because the + // command-line argument parsing library should be + // responsible for ensuring each comma-separated + // string is a valid column label. + Self::parse(s).unwrap() + }) + .collect() + } + (false, true, false) => vec![ Self::Source, Self::Itotal, Self::Iused, @@ -84,7 +98,7 @@ impl Column { Self::Ipcent, Self::Target, ], - (true, false) => vec![ + (true, false, false) => vec![ Self::Source, Self::Fstype, Self::Size, @@ -95,7 +109,7 @@ impl Column { Self::Pcent, Self::Target, ], - (true, true) => vec![ + (true, true, false) => vec![ Self::Source, Self::Fstype, Self::Itotal, @@ -104,6 +118,49 @@ impl Column { Self::Ipcent, Self::Target, ], + // The command-line arguments -T and -i are each mutually + // exclusive with --output, so the command-line argument + // parser should reject those combinations before we get + // to this point in the code. + _ => unreachable!(), + } + } + + /// Convert a column name to the corresponding enumeration variant. + /// + /// There are twelve valid column names, one for each variant: + /// + /// - "source" + /// - "fstype" + /// - "itotal" + /// - "iused" + /// - "iavail" + /// - "ipcent" + /// - "size" + /// - "used" + /// - "avail" + /// - "pcent" + /// - "file" + /// - "target" + /// + /// # Errors + /// + /// If the string `s` is not one of the valid column names. + fn parse(s: &str) -> Result { + match s { + "source" => Ok(Self::Source), + "fstype" => Ok(Self::Fstype), + "itotal" => Ok(Self::Itotal), + "iused" => Ok(Self::Iused), + "iavail" => Ok(Self::Iavail), + "ipcent" => Ok(Self::Ipcent), + "size" => Ok(Self::Size), + "used" => Ok(Self::Used), + "avail" => Ok(Self::Avail), + "pcent" => Ok(Self::Pcent), + "file" => Ok(Self::File), + "target" => Ok(Self::Target), + _ => Err(()), } } } diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 8f8474901..5d99b648b 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -183,4 +183,33 @@ fn test_block_size_1024() { assert_eq!(get_header(34 * 1024 * 1024 * 1024), "34G-blocks"); } +// TODO The spacing does not match GNU df. Also we need to remove +// trailing spaces from the heading row. +#[test] +fn test_output_selects_columns() { + let output = new_ucmd!() + .args(&["--output=source"]) + .succeeds() + .stdout_move_str(); + assert_eq!(output.lines().next().unwrap(), "Filesystem "); + + let output = new_ucmd!() + .args(&["--output=source,target"]) + .succeeds() + .stdout_move_str(); + assert_eq!( + output.lines().next().unwrap(), + "Filesystem Mounted on " + ); + + let output = new_ucmd!() + .args(&["--output=source,target,used"]) + .succeeds() + .stdout_move_str(); + assert_eq!( + output.lines().next().unwrap(), + "Filesystem Mounted on Used " + ); +} + // ToDO: more tests... From c0cd0177064695697ba24b29689057618c8faa43 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 21 Feb 2022 20:50:55 -0500 Subject: [PATCH 45/60] tests: update test_df.rs to use --output argument --- tests/by-util/test_df.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 5d99b648b..896a55931 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -43,21 +43,14 @@ fn test_df_output() { /// Test that the order of rows in the table does not change across executions. #[test] fn test_order_same() { - // TODO When #3057 is resolved, we should just use - // - // new_ucmd!().arg("--output=source").succeeds().stdout_move_str(); - // - // instead of parsing the entire `df` table as a string. - let output1 = new_ucmd!().succeeds().stdout_move_str(); - let output2 = new_ucmd!().succeeds().stdout_move_str(); - let output1: Vec = output1 - .lines() - .map(|l| String::from(l.split_once(' ').unwrap().0)) - .collect(); - let output2: Vec = output2 - .lines() - .map(|l| String::from(l.split_once(' ').unwrap().0)) - .collect(); + let output1 = new_ucmd!() + .arg("--output=source") + .succeeds() + .stdout_move_str(); + let output2 = new_ucmd!() + .arg("--output=source") + .succeeds() + .stdout_move_str(); assert_eq!(output1, output2); } From 77d92883c75f37d4f2c5f22c133b029d62835182 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 1 Jan 2022 23:53:29 -0500 Subject: [PATCH 46/60] split: implement --line-bytes option Implement the `--line-bytes` option to `split`. In this mode, the program tries to write as many lines of the input as possible to each chunk of output without exceeding a specified byte limit. The new `LineBytesChunkWriter` struct represents this functionality. --- src/uu/split/src/split.rs | 204 ++++++++++++++++++++++++++++++- tests/by-util/test_split.rs | 12 +- tests/fixtures/split/letters.txt | 5 + 3 files changed, 218 insertions(+), 3 deletions(-) create mode 100644 tests/fixtures/split/letters.txt diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index 090d89d4e..73abc966b 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -5,7 +5,7 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -// spell-checker:ignore (ToDO) PREFIXaa +// spell-checker:ignore (ToDO) PREFIXaa nbbbb ncccc mod filenames; mod number; @@ -760,6 +760,187 @@ impl<'a> Write for LineChunkWriter<'a> { } } +/// Write lines to each sequential output files, limited by bytes. +/// +/// This struct maintains an underlying writer representing the +/// current chunk of the output. On each call to [`write`], it writes +/// as many lines as possible to the current chunk without exceeding +/// the specified byte limit. If a single line has more bytes than the +/// limit, then fill an entire single chunk with those bytes and +/// handle the remainder of the line as if it were its own distinct +/// line. As many new underlying writers are created as needed to +/// write all the data in the input buffer. +struct LineBytesChunkWriter<'a> { + /// Parameters for creating the underlying writer for each new chunk. + settings: &'a Settings, + + /// The maximum number of bytes allowed for a single chunk of output. + chunk_size: u64, + + /// Running total of number of chunks that have been completed. + num_chunks_written: usize, + + /// Remaining capacity in number of bytes in the current chunk. + /// + /// This number starts at `chunk_size` and decreases as lines are + /// written. Once it reaches zero, a writer for a new chunk is + /// initialized and this number gets reset to `chunk_size`. + num_bytes_remaining_in_current_chunk: usize, + + /// The underlying writer for the current chunk. + /// + /// Once the number of bytes written to this writer exceeds + /// `chunk_size`, a new writer is initialized and assigned to this + /// field. + inner: BufWriter>, + + /// Iterator that yields filenames for each chunk. + filename_iterator: FilenameIterator<'a>, +} + +impl<'a> LineBytesChunkWriter<'a> { + fn new(chunk_size: u64, settings: &'a Settings) -> Option> { + let mut filename_iterator = FilenameIterator::new( + &settings.prefix, + &settings.additional_suffix, + settings.suffix_length, + settings.suffix_type, + ); + let filename = filename_iterator.next()?; + if settings.verbose { + println!("creating file {}", filename.quote()); + } + let inner = platform::instantiate_current_writer(&settings.filter, &filename); + Some(LineBytesChunkWriter { + settings, + chunk_size, + num_bytes_remaining_in_current_chunk: chunk_size.try_into().unwrap(), + num_chunks_written: 0, + inner, + filename_iterator, + }) + } +} + +impl<'a> Write for LineBytesChunkWriter<'a> { + /// Write as many lines to a chunk as possible without + /// exceeding the byte limit. If a single line has more bytes + /// than the limit, then fill an entire single chunk with those + /// bytes and handle the remainder of the line as if it were + /// its own distinct line. + /// + /// For example: if the `chunk_size` is 8 and the input is: + /// + /// ```text + /// aaaaaaaaa\nbbbb\ncccc\ndd\nee\n + /// ``` + /// + /// then the output gets broken into chunks like this: + /// + /// ```text + /// chunk 0 chunk 1 chunk 2 chunk 3 + /// + /// 0 1 2 + /// 01234567 89 01234 56789 012 345 6 + /// |------| |-------| |--------| |---| + /// aaaaaaaa a\nbbbb\n cccc\ndd\n ee\n + /// ``` + fn write(&mut self, mut buf: &[u8]) -> std::io::Result { + // The total number of bytes written during the loop below. + // + // It is necessary to keep this running total because we may + // be making multiple calls to `write()` on multiple different + // underlying writers and we want the final reported number of + // bytes written to reflect the total number of bytes written + // to all of the underlying writers. + let mut total_bytes_written = 0; + + // Loop until we have written all bytes in the input buffer + // (or an IO error occurs). + loop { + // If we have filled the current chunk with bytes, then + // start a new chunk and initialize its corresponding + // writer. + if self.num_bytes_remaining_in_current_chunk == 0 { + self.num_chunks_written += 1; + let filename = self.filename_iterator.next().ok_or_else(|| { + std::io::Error::new(ErrorKind::Other, "output file suffixes exhausted") + })?; + if self.settings.verbose { + println!("creating file {}", filename.quote()); + } + self.inner = platform::instantiate_current_writer(&self.settings.filter, &filename); + self.num_bytes_remaining_in_current_chunk = self.chunk_size.try_into().unwrap(); + } + + // Find the first newline character in the buffer. + match memchr::memchr(b'\n', buf) { + // If there is no newline character and the buffer is + // empty, then we are done writing. + None if buf.is_empty() => { + return Ok(total_bytes_written); + } + + // If there is no newline character and the buffer is + // not empty, then write as many bytes as we can and + // then move on to the next chunk if necessary. + None => { + let end = self.num_bytes_remaining_in_current_chunk; + let num_bytes_written = self.inner.write(&buf[..end])?; + self.num_bytes_remaining_in_current_chunk -= num_bytes_written; + total_bytes_written += num_bytes_written; + buf = &buf[num_bytes_written..]; + } + + // If there is a newline character and the line + // (including the newline character) will fit in the + // current chunk, then write the entire line and + // continue to the next iteration. (See chunk 1 in the + // example comment above.) + Some(i) if i < self.num_bytes_remaining_in_current_chunk => { + let num_bytes_written = self.inner.write(&buf[..i + 1])?; + self.num_bytes_remaining_in_current_chunk -= num_bytes_written; + total_bytes_written += num_bytes_written; + buf = &buf[num_bytes_written..]; + } + + // If there is a newline character, the line + // (including the newline character) will not fit in + // the current chunk, *and* no other lines have been + // written to the current chunk, then write as many + // bytes as we can and continue to the next + // iteration. (See chunk 0 in the example comment + // above.) + Some(_) + if self.num_bytes_remaining_in_current_chunk + == self.chunk_size.try_into().unwrap() => + { + let end = self.num_bytes_remaining_in_current_chunk; + let num_bytes_written = self.inner.write(&buf[..end])?; + self.num_bytes_remaining_in_current_chunk -= num_bytes_written; + total_bytes_written += num_bytes_written; + buf = &buf[num_bytes_written..]; + } + + // If there is a newline character, the line + // (including the newline character) will not fit in + // the current chunk, and at least one other line has + // been written to the current chunk, then signal to + // the next iteration that a new chunk needs to be + // created and continue to the next iteration of the + // loop to try writing the line there. + Some(_) => { + self.num_bytes_remaining_in_current_chunk = 0; + } + } + } + } + + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + /// Split a file into a specific number of chunks by byte. /// /// This function always creates one output file for each chunk, even @@ -1027,7 +1208,7 @@ fn split(settings: &Settings) -> UResult<()> { }, } } - Strategy::Bytes(chunk_size) | Strategy::LineBytes(chunk_size) => { + Strategy::Bytes(chunk_size) => { let mut writer = ByteChunkWriter::new(chunk_size, settings) .ok_or_else(|| USimpleError::new(1, "output file suffixes exhausted"))?; match std::io::copy(&mut reader, &mut writer) { @@ -1046,6 +1227,25 @@ fn split(settings: &Settings) -> UResult<()> { }, } } + Strategy::LineBytes(chunk_size) => { + let mut writer = LineBytesChunkWriter::new(chunk_size, settings) + .ok_or_else(|| USimpleError::new(1, "output file suffixes exhausted"))?; + match std::io::copy(&mut reader, &mut writer) { + Ok(_) => Ok(()), + Err(e) => match e.kind() { + // TODO Since the writer object controls the creation of + // new files, we need to rely on the `std::io::Result` + // returned by its `write()` method to communicate any + // errors to this calling scope. If a new file cannot be + // created because we have exceeded the number of + // allowable filenames, we use `ErrorKind::Other` to + // indicate that. A special error message needs to be + // printed in that case. + ErrorKind::Other => Err(USimpleError::new(1, "output file suffixes exhausted")), + _ => Err(uio_error!(e, "input/output error")), + }, + } + } } } diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index 08431a8f6..642cb7c68 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -2,7 +2,7 @@ // * // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -// spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes threebytes asciilowercase fghij klmno pqrst uvwxyz fivelines twohundredfortyonebytes onehundredlines +// spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes threebytes asciilowercase fghij klmno pqrst uvwxyz fivelines twohundredfortyonebytes onehundredlines nbbbb extern crate rand; extern crate regex; @@ -595,3 +595,13 @@ fn test_lines_kth() { .succeeds() .stdout_only("20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n"); } + +#[test] +fn test_line_bytes() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["-C", "8", "letters.txt"]).succeeds(); + assert_eq!(at.read("xaa"), "aaaaaaaa"); + assert_eq!(at.read("xab"), "a\nbbbb\n"); + assert_eq!(at.read("xac"), "cccc\ndd\n"); + assert_eq!(at.read("xad"), "ee\n"); +} diff --git a/tests/fixtures/split/letters.txt b/tests/fixtures/split/letters.txt new file mode 100644 index 000000000..03e1003e3 --- /dev/null +++ b/tests/fixtures/split/letters.txt @@ -0,0 +1,5 @@ +aaaaaaaaa +bbbb +cccc +dd +ee From 5c5f4ca6ad80a46dd8d2fa5db775698edfccd16d Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Fri, 11 Mar 2022 14:06:34 +0530 Subject: [PATCH 47/60] `df`: Adds support for mount path prefix matching and input path (#3161) * Adds support for mount path prefix matching and input path canonicalization - Sorts mount paths in reverse lexicographical order - Canonicalize all paths and clear invalid paths - Checking of mount path prefix matches input path --- src/uu/df/src/df.rs | 59 ++++++++++++++++++++++++++++------------ tests/by-util/test_df.rs | 12 ++++++++ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 62e669282..715e24541 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -5,7 +5,7 @@ // // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. -// spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs +// spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs lofs mod blocks; mod columns; mod table; @@ -22,7 +22,6 @@ use std::collections::HashSet; use std::fmt; use std::iter::FromIterator; -#[cfg(windows)] use std::path::Path; use crate::blocks::{block_size_from_matches, BlockSize}; @@ -188,7 +187,7 @@ impl Filesystem { } /// Whether to display the mount info given the inclusion settings. -fn is_included(mi: &MountInfo, paths: &[String], opt: &Options) -> bool { +fn is_included(mi: &MountInfo, opt: &Options) -> bool { // Don't show remote filesystems if `--local` has been given. if mi.remote && opt.show_local_fs { return false; @@ -204,12 +203,6 @@ fn is_included(mi: &MountInfo, paths: &[String], opt: &Options) -> bool { return false; } - // Don't show filesystems other than the ones specified on the - // command line, if any. - if !paths.is_empty() && !paths.contains(&mi.mount_dir) { - return false; - } - true } @@ -259,15 +252,13 @@ fn is_best(previous: &[MountInfo], mi: &MountInfo) -> bool { /// Keep only the specified subset of [`MountInfo`] instances. /// -/// If `paths` is non-empty, this function excludes any [`MountInfo`] -/// that is not mounted at the specified path. -/// /// The `opt` argument specifies a variety of ways of excluding /// [`MountInfo`] instances; see [`Options`] for more information. /// /// Finally, if there are duplicate entries, the one with the shorter /// path is kept. -fn filter_mount_list(vmi: Vec, paths: &[String], opt: &Options) -> Vec { + +fn filter_mount_list(vmi: Vec, opt: &Options) -> Vec { let mut result = vec![]; for mi in vmi { // TODO The running time of the `is_best()` function is linear @@ -275,21 +266,45 @@ fn filter_mount_list(vmi: Vec, paths: &[String], opt: &Options) -> Ve // this loop quadratic in the length of `vmi`. This could be // improved by a more efficient implementation of `is_best()`, // but `vmi` is probably not very long in practice. - if is_included(&mi, paths, opt) && is_best(&result, &mi) { + if is_included(&mi, opt) && is_best(&result, &mi) { result.push(mi); } } result } +/// Assign 1 `MountInfo` entry to each path +/// `lofs` entries are skipped and dummy mount points are skipped +/// Only the longest matching prefix for that path is considered +/// `lofs` is for Solaris style loopback filesystem and is present in Solaris and FreeBSD. +/// It works similar to symlinks +fn get_point_list(vmi: &[MountInfo], paths: &[String]) -> Vec { + paths + .iter() + .map(|p| { + vmi.iter() + .filter(|mi| mi.fs_type.ne("lofs")) + .filter(|mi| !mi.dummy) + .filter(|mi| p.starts_with(&mi.mount_dir)) + .max_by_key(|mi| mi.mount_dir.len()) + .unwrap() + .clone() + }) + .collect::>() +} + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().get_matches_from(args); - let paths: Vec = matches + // Canonicalize the input_paths and then convert to string + let paths = matches .values_of(OPT_PATHS) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_default(); + .unwrap_or_default() + .map(Path::new) + .filter_map(|v| v.canonicalize().ok()) + .filter_map(|v| v.into_os_string().into_string().ok()) + .collect::>(); #[cfg(windows)] { @@ -302,7 +317,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let opt = Options::from(&matches).map_err(|e| USimpleError::new(1, format!("{}", e)))?; let mounts = read_fs_list(); - let data: Vec = filter_mount_list(mounts, &paths, &opt) + + let op_mount_points: Vec = if paths.is_empty() { + // Get all entries + filter_mount_list(mounts, &opt) + } else { + // Get Point for each input_path + get_point_list(&mounts, &paths) + }; + let data: Vec = op_mount_points .into_iter() .filter_map(Filesystem::new) .filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs) diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 896a55931..a096b4b4c 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -54,6 +54,18 @@ fn test_order_same() { assert_eq!(output1, output2); } +/// Test of mount point begin repeated +#[cfg(unix)] +#[test] +fn test_output_mp_repeat() { + let output1 = new_ucmd!().arg("/").arg("/").succeeds().stdout_move_str(); + let output1: Vec = output1 + .lines() + .map(|l| String::from(l.split_once(' ').unwrap().0)) + .collect(); + assert_eq!(3, output1.len()); + assert_eq!(output1[1], output1[2]); +} #[test] fn test_output_conflict_options() { for option in ["-i", "-T", "-P"] { From ebd0c0954957c3225778620ee203ba6dd3b7bed6 Mon Sep 17 00:00:00 2001 From: jfinkels Date: Fri, 11 Mar 2022 03:38:06 -0500 Subject: [PATCH 48/60] util: use seq to enumerate integers (#3239) Replace a bash loop for enumerating integers with a more concise use of `seq` in `util/build-gnu.sh`. --- util/build-gnu.sh | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index f2352f7c0..adf69e3be 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -70,33 +70,17 @@ sed -i 's|^"\$@|/usr/bin/timeout 600 "\$@|' build-aux/test-driver sed -i "s/^[[:blank:]]*PATH=.*/ PATH='${UU_BUILD_DIR//\//\\/}\$(PATH_SEPARATOR)'\"\$\$PATH\" \\\/" Makefile sed -i 's| tr | /usr/bin/tr |' tests/init.sh make -j "$(nproc)" -first=00 if test ${UU_MAKE_PROFILE} != "debug"; then # Generate the factor tests, so they can be fixed # * reduced to 20 to decrease log size (down from 36 expected by GNU) # * only for 'release', skipped for 'debug' as redundant and too time consuming (causing timeout errors) - seq=$( - i=${first} - while test "$i" -le 20; do - printf '%02d ' $i - i=$(($i + 1)) - done - ) - for i in ${seq}; do + for i in $(seq -w 0 20); do make "tests/factor/t${i}.sh" done sed -i -e 's|sha1sum |/usr/bin/sha1sum |' tests/factor/t*sh - first=21 fi # strip all (debug) or just the longer (release) factor tests from Makefile -seq=$( - i=${first} - while test "$i" -le 36; do - printf '%02d ' $i - i=$(($i + 1)) - done -) -for i in ${seq}; do +for i in $(seq 20 36); do echo "strip t${i}.sh from Makefile" sed -i -e "s/\$(tf)\/t${i}.sh//g" Makefile done From a0eed2405c34de9dcafd86e687b737e1ae408ba2 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Thu, 10 Mar 2022 22:06:19 -0500 Subject: [PATCH 49/60] timeout: refactor helper func for wait-then-signal Factor a helper function `wait_or_kill_process()` out of the main `timeout()` function. This helper function contains the code to wait for a child process and send the `SIGKILL` signal if it does not terminate within a specified amount of time. This does not change the function of `timeout`, just the organization of the code. --- src/uu/timeout/src/timeout.rs | 132 +++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 48 deletions(-) diff --git a/src/uu/timeout/src/timeout.rs b/src/uu/timeout/src/timeout.rs index 2493442b2..d90907fae 100644 --- a/src/uu/timeout/src/timeout.rs +++ b/src/uu/timeout/src/timeout.rs @@ -14,7 +14,7 @@ extern crate clap; use clap::{crate_version, App, AppSettings, Arg}; use std::io::ErrorKind; -use std::process::{Command, Stdio}; +use std::process::{Child, Command, Stdio}; use std::time::Duration; use uucore::display::Quotable; use uucore::error::{UResult, USimpleError}; @@ -177,6 +177,60 @@ fn unblock_sigchld() { } } +/// Report that a signal is being sent if the verbose flag is set. +fn report_if_verbose(signal: usize, cmd: &str, verbose: bool) { + if verbose { + let s = signal_name_by_value(signal).unwrap(); + show_error!("sending signal {} to command {}", s, cmd.quote()); + } +} + +/// Wait for a child process and send a kill signal if it does not terminate. +/// +/// This function waits for the child `process` for the time period +/// given by `duration`. If the child process does not terminate +/// within that time, we send the `SIGKILL` signal to it. If `verbose` +/// is `true`, then a message is printed to `stderr` when that +/// happens. +/// +/// If the child process terminates within the given time period and +/// `preserve_status` is `true`, then the status code of the child +/// process is returned. If the child process terminates within the +/// given time period and `preserve_status` is `false`, then 124 is +/// returned. If the child does not terminate within the time period, +/// then 137 is returned. Finally, if there is an error while waiting +/// for the child process to terminate, then 124 is returned. +/// +/// # Errors +/// +/// If there is a problem sending the `SIGKILL` signal or waiting for +/// the process after that signal is sent. +fn wait_or_kill_process( + mut process: Child, + cmd: &str, + duration: Duration, + preserve_status: bool, + verbose: bool, +) -> std::io::Result { + match process.wait_or_timeout(duration) { + Ok(Some(status)) => { + if preserve_status { + Ok(status.code().unwrap_or_else(|| status.signal().unwrap())) + } else { + Ok(124) + } + } + Ok(None) => { + let signal = signal_by_name_or_value("KILL").unwrap(); + report_if_verbose(signal, cmd, verbose); + process.send_signal(signal)?; + process.wait()?; + Ok(137) + } + Err(_) => Ok(124), + } +} + /// TODO: Improve exit codes, and make them consistent with the GNU Coreutils exit codes. fn timeout( @@ -208,57 +262,39 @@ fn timeout( USimpleError::new(status_code, format!("failed to execute process: {}", err)) })?; unblock_sigchld(); + // Wait for the child process for the specified time period. + // + // If the process exits within the specified time period (the + // `Ok(Some(_))` arm), then return the appropriate status code. + // + // If the process does not exit within that time (the `Ok(None)` + // arm) and `kill_after` is specified, then try sending `SIGKILL`. + // + // TODO The structure of this block is extremely similar to the + // structure of `wait_or_kill_process()`. They can probably be + // refactored into some common function. match process.wait_or_timeout(duration) { - Ok(Some(status)) => { - let status_code = status.code().unwrap_or_else(|| status.signal().unwrap()); - if status_code == 0 { - Ok(()) - } else { - Err(status_code.into()) - } - } + Ok(Some(status)) => Err(status + .code() + .unwrap_or_else(|| status.signal().unwrap()) + .into()), Ok(None) => { - if verbose { - show_error!( - "sending signal {} to command {}", - signal_name_by_value(signal).unwrap(), - cmd[0].quote() - ); - } - process - .send_signal(signal) - .map_err(|e| USimpleError::new(ERR_EXIT_STATUS, format!("{}", e)))?; - if let Some(kill_after) = kill_after { - match process.wait_or_timeout(kill_after) { - Ok(Some(status)) => { - if preserve_status { - let status_code = - status.code().unwrap_or_else(|| status.signal().unwrap()); - if status_code == 0 { - Ok(()) - } else { - Err(status_code.into()) - } - } else { - Err(124.into()) - } + report_if_verbose(signal, &cmd[0], verbose); + process.send_signal(signal)?; + match kill_after { + None => Err(124.into()), + Some(kill_after) => { + match wait_or_kill_process( + process, + &cmd[0], + kill_after, + preserve_status, + verbose, + ) { + Ok(status) => Err(status.into()), + Err(e) => Err(USimpleError::new(ERR_EXIT_STATUS, format!("{}", e))), } - Ok(None) => { - if verbose { - show_error!("sending signal KILL to command {}", cmd[0].quote()); - } - process - .send_signal(uucore::signals::signal_by_name_or_value("KILL").unwrap()) - .map_err(|e| USimpleError::new(ERR_EXIT_STATUS, format!("{}", e)))?; - process - .wait() - .map_err(|e| USimpleError::new(ERR_EXIT_STATUS, format!("{}", e)))?; - Err(137.into()) - } - Err(_) => Err(124.into()), } - } else { - Err(124.into()) } } Err(_) => { From 9fae445ca9d99a1471132efa07a06590ca490dc4 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Fri, 11 Mar 2022 22:52:17 -0500 Subject: [PATCH 50/60] df: fix tests of internal helper functions Fix unit tests for the `is_included()` and `filter_mount_list()` internal helper functions. The function signatures changed but the tests did not get updated to match. --- src/uu/df/src/df.rs | 63 ++++++++++----------------------------------- 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 715e24541..7b9b884e0 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -583,9 +583,8 @@ mod tests { #[test] fn test_remote_included() { let opt = Default::default(); - let paths = []; let m = mount_info("ext4", "/mnt/foo", true, false); - assert!(is_included(&m, &paths, &opt)); + assert!(is_included(&m, &opt)); } #[test] @@ -594,9 +593,8 @@ mod tests { show_local_fs: true, ..Default::default() }; - let paths = []; let m = mount_info("ext4", "/mnt/foo", true, false); - assert!(!is_included(&m, &paths, &opt)); + assert!(!is_included(&m, &opt)); } #[test] @@ -606,17 +604,15 @@ mod tests { show_listed_fs: true, ..Default::default() }; - let paths = []; let m = mount_info("ext4", "/mnt/foo", false, true); - assert!(is_included(&m, &paths, &opt)); + assert!(is_included(&m, &opt)); } #[test] fn test_dummy_excluded() { let opt = Default::default(); - let paths = []; let m = mount_info("ext4", "/mnt/foo", false, true); - assert!(!is_included(&m, &paths, &opt)); + assert!(!is_included(&m, &opt)); } #[test] @@ -630,9 +626,8 @@ mod tests { fs_selector, ..Default::default() }; - let paths = []; let m = mount_info("ext4", "/mnt/foo", false, false); - assert!(!is_included(&m, &paths, &opt)); + assert!(!is_included(&m, &opt)); } #[test] @@ -646,9 +641,8 @@ mod tests { fs_selector, ..Default::default() }; - let paths = []; let m = mount_info("ext4", "/mnt/foo", false, false); - assert!(is_included(&m, &paths, &opt)); + assert!(is_included(&m, &opt)); } #[test] @@ -662,9 +656,8 @@ mod tests { fs_selector, ..Default::default() }; - let paths = []; let m = mount_info("ext4", "/mnt/foo", false, false); - assert!(is_included(&m, &paths, &opt)); + assert!(is_included(&m, &opt)); } #[test] @@ -678,9 +671,8 @@ mod tests { fs_selector, ..Default::default() }; - let paths = []; let m = mount_info("ext4", "/mnt/foo", false, false); - assert!(!is_included(&m, &paths, &opt)); + assert!(!is_included(&m, &opt)); } #[test] @@ -692,9 +684,8 @@ mod tests { fs_selector, ..Default::default() }; - let paths = []; let m = mount_info("ext4", "/mnt/foo", false, false); - assert!(!is_included(&m, &paths, &opt)); + assert!(!is_included(&m, &opt)); } #[test] @@ -706,9 +697,8 @@ mod tests { fs_selector, ..Default::default() }; - let paths = []; let m = mount_info("ext4", "/mnt/foo", false, false); - assert!(!is_included(&m, &paths, &opt)); + assert!(!is_included(&m, &opt)); } #[test] @@ -720,9 +710,8 @@ mod tests { fs_selector, ..Default::default() }; - let paths = []; let m = mount_info("ext4", "/mnt/foo", false, false); - assert!(is_included(&m, &paths, &opt)); + assert!(is_included(&m, &opt)); } #[test] @@ -737,33 +726,8 @@ mod tests { fs_selector, ..Default::default() }; - let paths = []; let m = mount_info("ext4", "/mnt/foo", false, false); - assert!(!is_included(&m, &paths, &opt)); - } - - #[test] - fn test_paths_empty() { - let opt = Default::default(); - let paths = []; - let m = mount_info("ext4", "/mnt/foo", false, false); - assert!(is_included(&m, &paths, &opt)); - } - - #[test] - fn test_not_in_paths() { - let opt = Default::default(); - let paths = [String::from("/mnt/foo")]; - let m = mount_info("ext4", "/mnt/bar", false, false); - assert!(!is_included(&m, &paths, &opt)); - } - - #[test] - fn test_in_paths() { - let opt = Default::default(); - let paths = [String::from("/mnt/foo")]; - let m = mount_info("ext4", "/mnt/foo", false, false); - assert!(is_included(&m, &paths, &opt)); + assert!(!is_included(&m, &opt)); } } @@ -774,9 +738,8 @@ mod tests { #[test] fn test_empty() { let opt = Default::default(); - let paths = []; let mount_infos = vec![]; - assert!(filter_mount_list(mount_infos, &paths, &opt).is_empty()); + assert!(filter_mount_list(mount_infos, &opt).is_empty()); } } } From 094198b22a4663d66d485e4a4ac9cfdc7928502d Mon Sep 17 00:00:00 2001 From: OHNONOTAMOTH <53664669+OHNONOTAMOTH@users.noreply.github.com> Date: Sat, 12 Mar 2022 03:40:23 -0500 Subject: [PATCH 51/60] Consistent tempfile crate declaration (version 3) (#3244) --- Cargo.toml | 2 +- src/uu/dd/Cargo.toml | 2 +- src/uu/mktemp/Cargo.toml | 2 +- src/uu/stdbuf/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4222f1749..43797e71e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -375,7 +375,7 @@ pretty_assertions = "1" rand = "0.8" regex = "1.0" sha1 = { version="0.10", features=["std"] } -tempfile = "3.2.0" +tempfile = "3" time = "0.1" unindent = "0.1" uucore = { version=">=0.0.11", package="uucore", path="src/uucore", features=["entries", "process"] } diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index b0392e14d..139a4845d 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -22,7 +22,7 @@ libc = "0.2" uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } [dev-dependencies] -tempfile = "^3" +tempfile = "3" [target.'cfg(target_os = "linux")'.dependencies] signal-hook = "0.3.9" diff --git a/src/uu/mktemp/Cargo.toml b/src/uu/mktemp/Cargo.toml index eb25702d3..1ae00fa6f 100644 --- a/src/uu/mktemp/Cargo.toml +++ b/src/uu/mktemp/Cargo.toml @@ -17,7 +17,7 @@ path = "src/mktemp.rs" [dependencies] clap = { version = "3.0", features = ["wrap_help", "cargo"] } rand = "0.8" -tempfile = "3.1" +tempfile = "3" uucore = { version=">=0.0.11", package="uucore", path="../../uucore" } [[bin]] diff --git a/src/uu/stdbuf/Cargo.toml b/src/uu/stdbuf/Cargo.toml index c7942779d..a977adfe6 100644 --- a/src/uu/stdbuf/Cargo.toml +++ b/src/uu/stdbuf/Cargo.toml @@ -16,7 +16,7 @@ path = "src/stdbuf.rs" [dependencies] clap = { version = "3.0", features = ["wrap_help", "cargo"] } -tempfile = "3.1" +tempfile = "3" uucore = { version=">=0.0.11", package="uucore", path="../../uucore" } [build-dependencies] From 2c6bbcf71628d20d1cec270a3479d58b098771f3 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 12 Mar 2022 00:33:18 +0100 Subject: [PATCH 52/60] nproc: support --ignore=' 1' as GNU --- src/uu/nproc/src/nproc.rs | 2 +- tests/by-util/test_nproc.rs | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/uu/nproc/src/nproc.rs b/src/uu/nproc/src/nproc.rs index de8f0c3ed..72ae9cd65 100644 --- a/src/uu/nproc/src/nproc.rs +++ b/src/uu/nproc/src/nproc.rs @@ -33,7 +33,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().get_matches_from(args); let mut ignore = match matches.value_of(OPT_IGNORE) { - Some(numstr) => match numstr.parse() { + Some(numstr) => match numstr.trim().parse() { Ok(num) => num, Err(e) => { return Err(USimpleError::new( diff --git a/tests/by-util/test_nproc.rs b/tests/by-util/test_nproc.rs index abf758829..43cfaa2d7 100644 --- a/tests/by-util/test_nproc.rs +++ b/tests/by-util/test_nproc.rs @@ -33,15 +33,22 @@ fn test_nproc_all_omp() { #[test] fn test_nproc_ignore() { let result = new_ucmd!().succeeds(); - let nproc: u8 = result.stdout_str().trim().parse().unwrap(); - if nproc > 1 { + let nproc_total: u8 = result.stdout_str().trim().parse().unwrap(); + if nproc_total > 1 { // Ignore all CPU but one let result = TestScenario::new(util_name!()) .ucmd_keepenv() .arg("--ignore") - .arg((nproc - 1).to_string()) + .arg((nproc_total - 1).to_string()) .succeeds(); let nproc: u8 = result.stdout_str().trim().parse().unwrap(); assert!(nproc == 1); + // Ignore all CPU but one with a string + let result = TestScenario::new(util_name!()) + .ucmd_keepenv() + .arg("--ignore= 1") + .succeeds(); + let nproc: u8 = result.stdout_str().trim().parse().unwrap(); + assert!(nproc_total - 1 == nproc); } } From 2d390233fef14434887db76ff388530df24a2e02 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Fri, 11 Mar 2022 22:33:25 -0500 Subject: [PATCH 53/60] timeout: add ExitStatus enumeration Add the `ExitStatus` enumeration in a new module `status.rs` to contain all the magic numbers in the `timeout` utility. --- src/uu/timeout/src/status.rs | 51 +++++++++++++++++++++++++++++++++++ src/uu/timeout/src/timeout.rs | 25 +++++++++-------- 2 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 src/uu/timeout/src/status.rs diff --git a/src/uu/timeout/src/status.rs b/src/uu/timeout/src/status.rs new file mode 100644 index 000000000..9a8558954 --- /dev/null +++ b/src/uu/timeout/src/status.rs @@ -0,0 +1,51 @@ +// * This file is part of the uutils coreutils package. +// * +// * For the full copyright and license information, please view the LICENSE +// * file that was distributed with this source code. +//! Exit status codes produced by `timeout`. +use std::convert::From; +use uucore::error::UError; + +/// Enumerates the exit statuses produced by `timeout`. +/// +/// Use [`Into::into`] (or [`From::from`]) to convert an enumeration +/// member into a numeric status code. You can also convert into a +/// [`UError`]. +/// +/// # Examples +/// +/// Convert into an [`i32`]: +/// +/// ```rust,ignore +/// assert_eq!(i32::from(ExitStatus::CommandTimedOut), 124); +/// ``` +pub(crate) enum ExitStatus { + /// When the child process times out and `--preserve-status` is not specified. + CommandTimedOut, + + /// When `timeout` itself fails. + TimeoutFailed, + + /// When a signal is sent to the child process or `timeout` itself. + SignalSent(usize), + + /// When there is a failure while waiting for the child process to terminate. + WaitingFailed, +} + +impl From for i32 { + fn from(exit_status: ExitStatus) -> Self { + match exit_status { + ExitStatus::CommandTimedOut => 124, + ExitStatus::TimeoutFailed => 125, + ExitStatus::SignalSent(s) => 128 + s as Self, + ExitStatus::WaitingFailed => 124, + } + } +} + +impl From for Box { + fn from(exit_status: ExitStatus) -> Self { + Box::from(i32::from(exit_status)) + } +} diff --git a/src/uu/timeout/src/timeout.rs b/src/uu/timeout/src/timeout.rs index d90907fae..f02f0a0a9 100644 --- a/src/uu/timeout/src/timeout.rs +++ b/src/uu/timeout/src/timeout.rs @@ -6,12 +6,14 @@ // * file that was distributed with this source code. // spell-checker:ignore (ToDO) tstr sigstr cmdname setpgid sigchld +mod status; #[macro_use] extern crate uucore; extern crate clap; +use crate::status::ExitStatus; use clap::{crate_version, App, AppSettings, Arg}; use std::io::ErrorKind; use std::process::{Child, Command, Stdio}; @@ -25,8 +27,6 @@ use uucore::{format_usage, InvalidEncodingHandling}; static ABOUT: &str = "Start COMMAND, and kill it if still running after DURATION."; const USAGE: &str = "{} [OPTION] DURATION COMMAND..."; -const ERR_EXIT_STATUS: i32 = 125; - pub mod options { pub static FOREGROUND: &str = "foreground"; pub static KILL_AFTER: &str = "kill-after"; @@ -217,7 +217,7 @@ fn wait_or_kill_process( if preserve_status { Ok(status.code().unwrap_or_else(|| status.signal().unwrap())) } else { - Ok(124) + Ok(ExitStatus::TimeoutFailed.into()) } } Ok(None) => { @@ -225,9 +225,9 @@ fn wait_or_kill_process( report_if_verbose(signal, cmd, verbose); process.send_signal(signal)?; process.wait()?; - Ok(137) + Ok(ExitStatus::SignalSent(signal).into()) } - Err(_) => Ok(124), + Err(_) => Ok(ExitStatus::WaitingFailed.into()), } } @@ -282,7 +282,7 @@ fn timeout( report_if_verbose(signal, &cmd[0], verbose); process.send_signal(signal)?; match kill_after { - None => Err(124.into()), + None => Err(ExitStatus::CommandTimedOut.into()), Some(kill_after) => { match wait_or_kill_process( process, @@ -292,7 +292,10 @@ fn timeout( verbose, ) { Ok(status) => Err(status.into()), - Err(e) => Err(USimpleError::new(ERR_EXIT_STATUS, format!("{}", e))), + Err(e) => Err(USimpleError::new( + ExitStatus::TimeoutFailed.into(), + format!("{}", e), + )), } } } @@ -301,10 +304,10 @@ fn timeout( // We're going to return ERR_EXIT_STATUS regardless of // whether `send_signal()` succeeds or fails, so just // ignore the return value. - process - .send_signal(signal) - .map_err(|e| USimpleError::new(ERR_EXIT_STATUS, format!("{}", e)))?; - Err(ERR_EXIT_STATUS.into()) + process.send_signal(signal).map_err(|e| { + USimpleError::new(ExitStatus::TimeoutFailed.into(), format!("{}", e)) + })?; + Err(ExitStatus::TimeoutFailed.into()) } } } From 1aa6fd14680772e6a140d3c236a549b5e4014348 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Fri, 11 Mar 2022 22:40:47 -0500 Subject: [PATCH 54/60] timeout: fix bug in --preserve-status mode Fix a bug where `timeout --preserve-status` was not correctly preserving the status code of the child process if it timed out. When that happens, the status code of the child process is considered to be the signal number (in this case, `SIGTERM`). The exit status of `timeout` is then 128 plus the numeric code associated with `SIGTERM`. --- src/uu/timeout/src/timeout.rs | 8 +++++++- tests/by-util/test_timeout.rs | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/uu/timeout/src/timeout.rs b/src/uu/timeout/src/timeout.rs index f02f0a0a9..3da0bcd2a 100644 --- a/src/uu/timeout/src/timeout.rs +++ b/src/uu/timeout/src/timeout.rs @@ -282,7 +282,13 @@ fn timeout( report_if_verbose(signal, &cmd[0], verbose); process.send_signal(signal)?; match kill_after { - None => Err(ExitStatus::CommandTimedOut.into()), + None => { + if preserve_status { + Err(ExitStatus::SignalSent(signal).into()) + } else { + Err(ExitStatus::CommandTimedOut.into()) + } + } Some(kill_after) => { match wait_or_kill_process( process, diff --git a/tests/by-util/test_timeout.rs b/tests/by-util/test_timeout.rs index 5db91a44d..b9c8a59ed 100644 --- a/tests/by-util/test_timeout.rs +++ b/tests/by-util/test_timeout.rs @@ -53,3 +53,14 @@ fn test_command_empty_args() { .fails() .stderr_contains("timeout: empty string"); } + +#[test] +fn test_preserve_status() { + new_ucmd!() + .args(&["--preserve-status", ".1", "sleep", "10"]) + .fails() + // 128 + SIGTERM = 128 + 15 + .code_is(128 + 15) + .no_stderr() + .no_stdout(); +} From 81e2de498a23c2ec473ab5f65ef886d6b011d93f Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 12 Mar 2022 20:52:40 -0500 Subject: [PATCH 55/60] df: remove use of FsSelector abstraction Remove the use of the `FsSelector` struct when deciding whether a filesystem type should be included or excluded from the output table. Instead, just maintain optional `Vec`s of filesystem types to exclude and include, and check whether the filesystem type is contained in one of those. This reduces the amount of code required to implement these checks, and since the number of types given in the `include` or `exclude` lists is likely to be small, there should not be much of a difference in performance. --- src/uu/df/src/df.rs | 134 +++++++++++++++++--------------------------- 1 file changed, 52 insertions(+), 82 deletions(-) diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 7b9b884e0..854bc2a48 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -18,10 +18,7 @@ use uucore::fsext::{read_fs_list, FsUsage, MountInfo}; use clap::{crate_version, App, AppSettings, Arg, ArgMatches}; -use std::collections::HashSet; use std::fmt; -use std::iter::FromIterator; - use std::path::Path; use crate::blocks::{block_size_from_matches, BlockSize}; @@ -54,14 +51,6 @@ static OUTPUT_FIELD_LIST: [&str; 12] = [ "file", "target", ]; -/// Store names of file systems as a selector. -/// Note: `exclude` takes priority over `include`. -#[derive(Default)] -struct FsSelector { - include: HashSet, - exclude: HashSet, -} - /// Parameters that control the behavior of `df`. /// /// Most of these parameters control which rows and which columns are @@ -72,9 +61,22 @@ struct Options { show_all_fs: bool, show_listed_fs: bool, block_size: BlockSize, - fs_selector: FsSelector, + + /// Optional list of filesystem types to include in the output table. + /// + /// If this is not `None`, only filesystems that match one of + /// these types will be listed. + include: Option>, + + /// Optional list of filesystem types to exclude from the output table. + /// + /// If this is not `None`, filesystems that match one of these + /// types will *not* be listed. + exclude: Option>, + /// Whether to show a final row comprising the totals for each column. show_total: bool, + /// Sequence of columns to display in the output table. columns: Vec, } @@ -86,7 +88,8 @@ impl Default for Options { show_all_fs: Default::default(), show_listed_fs: Default::default(), block_size: Default::default(), - fs_selector: Default::default(), + include: Default::default(), + exclude: Default::default(), show_total: Default::default(), columns: vec![ Column::Source, @@ -125,7 +128,8 @@ impl Options { show_listed_fs: false, block_size: block_size_from_matches(matches) .map_err(|_| OptionsError::InvalidBlockSize)?, - fs_selector: FsSelector::from(matches), + include: matches.values_of_lossy(OPT_TYPE), + exclude: matches.values_of_lossy(OPT_EXCLUDE_TYPE), show_total: matches.is_present(OPT_TOTAL), columns: Column::from_matches(matches), }) @@ -138,30 +142,6 @@ struct Filesystem { usage: FsUsage, } -impl FsSelector { - /// Convert command-line arguments into a [`FsSelector`]. - /// - /// This function reads the include and exclude sets from - /// [`ArgMatches`] and returns the corresponding [`FsSelector`] - /// instance. - fn from(matches: &ArgMatches) -> Self { - let include = HashSet::from_iter(matches.values_of_lossy(OPT_TYPE).unwrap_or_default()); - let exclude = HashSet::from_iter( - matches - .values_of_lossy(OPT_EXCLUDE_TYPE) - .unwrap_or_default(), - ); - Self { include, exclude } - } - - fn should_select(&self, fs_type: &str) -> bool { - if self.exclude.contains(fs_type) { - return false; - } - self.include.is_empty() || self.include.contains(fs_type) - } -} - impl Filesystem { // TODO: resolve uuid in `mount_info.dev_name` if exists fn new(mount_info: MountInfo) -> Option { @@ -199,8 +179,15 @@ fn is_included(mi: &MountInfo, opt: &Options) -> bool { } // Don't show filesystems if they have been explicitly excluded. - if !opt.fs_selector.should_select(&mi.fs_type) { - return false; + if let Some(ref excludes) = opt.exclude { + if excludes.contains(&mi.fs_type) { + return false; + } + } + if let Some(ref includes) = opt.include { + if !includes.contains(&mi.fs_type) { + return false; + } } true @@ -562,8 +549,7 @@ mod tests { mod is_included { - use crate::{is_included, FsSelector, Options}; - use std::collections::HashSet; + use crate::{is_included, Options}; use uucore::fsext::MountInfo; /// Instantiate a [`MountInfo`] with the given fields. @@ -617,13 +603,9 @@ mod tests { #[test] fn test_exclude_match() { - let exclude = HashSet::from([String::from("ext4")]); - let fs_selector = FsSelector { - exclude, - ..Default::default() - }; + let exclude = Some(vec![String::from("ext4")]); let opt = Options { - fs_selector, + exclude, ..Default::default() }; let m = mount_info("ext4", "/mnt/foo", false, false); @@ -632,13 +614,9 @@ mod tests { #[test] fn test_exclude_no_match() { - let exclude = HashSet::from([String::from("tmpfs")]); - let fs_selector = FsSelector { - exclude, - ..Default::default() - }; + let exclude = Some(vec![String::from("tmpfs")]); let opt = Options { - fs_selector, + exclude, ..Default::default() }; let m = mount_info("ext4", "/mnt/foo", false, false); @@ -647,13 +625,9 @@ mod tests { #[test] fn test_include_match() { - let include = HashSet::from([String::from("ext4")]); - let fs_selector = FsSelector { - include, - ..Default::default() - }; + let include = Some(vec![String::from("ext4")]); let opt = Options { - fs_selector, + include, ..Default::default() }; let m = mount_info("ext4", "/mnt/foo", false, false); @@ -662,13 +636,9 @@ mod tests { #[test] fn test_include_no_match() { - let include = HashSet::from([String::from("tmpfs")]); - let fs_selector = FsSelector { - include, - ..Default::default() - }; + let include = Some(vec![String::from("tmpfs")]); let opt = Options { - fs_selector, + include, ..Default::default() }; let m = mount_info("ext4", "/mnt/foo", false, false); @@ -677,11 +647,11 @@ mod tests { #[test] fn test_include_and_exclude_match_neither() { - let include = HashSet::from([String::from("tmpfs")]); - let exclude = HashSet::from([String::from("squashfs")]); - let fs_selector = FsSelector { include, exclude }; + let include = Some(vec![String::from("tmpfs")]); + let exclude = Some(vec![String::from("squashfs")]); let opt = Options { - fs_selector, + include, + exclude, ..Default::default() }; let m = mount_info("ext4", "/mnt/foo", false, false); @@ -690,11 +660,11 @@ mod tests { #[test] fn test_include_and_exclude_match_exclude() { - let include = HashSet::from([String::from("tmpfs")]); - let exclude = HashSet::from([String::from("ext4")]); - let fs_selector = FsSelector { include, exclude }; + let include = Some(vec![String::from("tmpfs")]); + let exclude = Some(vec![String::from("ext4")]); let opt = Options { - fs_selector, + include, + exclude, ..Default::default() }; let m = mount_info("ext4", "/mnt/foo", false, false); @@ -703,11 +673,11 @@ mod tests { #[test] fn test_include_and_exclude_match_include() { - let include = HashSet::from([String::from("ext4")]); - let exclude = HashSet::from([String::from("squashfs")]); - let fs_selector = FsSelector { include, exclude }; + let include = Some(vec![String::from("ext4")]); + let exclude = Some(vec![String::from("squashfs")]); let opt = Options { - fs_selector, + include, + exclude, ..Default::default() }; let m = mount_info("ext4", "/mnt/foo", false, false); @@ -719,11 +689,11 @@ mod tests { // TODO The same filesystem type in both `include` and // `exclude` should cause an error, but currently does // not. - let include = HashSet::from([String::from("ext4")]); - let exclude = HashSet::from([String::from("ext4")]); - let fs_selector = FsSelector { include, exclude }; + let include = Some(vec![String::from("ext4")]); + let exclude = Some(vec![String::from("ext4")]); let opt = Options { - fs_selector, + include, + exclude, ..Default::default() }; let m = mount_info("ext4", "/mnt/foo", false, false); From 24b4af768c98616908c4757a2a30d7bc2b9a4ebc Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 13 Mar 2022 18:13:48 +0100 Subject: [PATCH 56/60] nproc: Improve the support of OMP_NUM_THREADS --- src/uu/nproc/src/nproc.rs | 20 ++++++++++---------- tests/by-util/test_nproc.rs | 24 ++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/uu/nproc/src/nproc.rs b/src/uu/nproc/src/nproc.rs index 72ae9cd65..f10b41e40 100644 --- a/src/uu/nproc/src/nproc.rs +++ b/src/uu/nproc/src/nproc.rs @@ -32,7 +32,7 @@ const USAGE: &str = "{} [OPTIONS]..."; pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().get_matches_from(args); - let mut ignore = match matches.value_of(OPT_IGNORE) { + let ignore = match matches.value_of(OPT_IGNORE) { Some(numstr) => match numstr.trim().parse() { Ok(num) => num, Err(e) => { @@ -45,18 +45,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { None => 0, }; - if !matches.is_present(OPT_ALL) { - // OMP_NUM_THREADS doesn't have an impact on --all - ignore += match env::var("OMP_NUM_THREADS") { - Ok(threadstr) => threadstr.parse().unwrap_or(0), - Err(_) => 0, - }; - } - let mut cores = if matches.is_present(OPT_ALL) { num_cpus_all() } else { - num_cpus::get() + // OMP_NUM_THREADS doesn't have an impact on --all + match env::var("OMP_NUM_THREADS") { + // Uses the OpenMP variable to force the number of threads + // If the parsing fails, returns the number of CPU + Ok(threadstr) => threadstr.parse().unwrap_or_else(|_| num_cpus::get()), + // the variable 'OMP_NUM_THREADS' doesn't exit + // fallback to the regular CPU detection + Err(_) => num_cpus::get(), + } }; if cores <= ignore { diff --git a/tests/by-util/test_nproc.rs b/tests/by-util/test_nproc.rs index 43cfaa2d7..5657e6b7e 100644 --- a/tests/by-util/test_nproc.rs +++ b/tests/by-util/test_nproc.rs @@ -1,3 +1,4 @@ +// spell-checker:ignore incorrectnumber use crate::common::util::*; #[test] @@ -15,11 +16,11 @@ fn test_nproc_all_omp() { let result = TestScenario::new(util_name!()) .ucmd_keepenv() - .env("OMP_NUM_THREADS", "1") + .env("OMP_NUM_THREADS", "60") .succeeds(); let nproc_omp: u8 = result.stdout_str().trim().parse().unwrap(); - assert!(nproc - 1 == nproc_omp); + assert!(nproc_omp == 60); let result = TestScenario::new(util_name!()) .ucmd_keepenv() @@ -28,6 +29,14 @@ fn test_nproc_all_omp() { .succeeds(); let nproc_omp: u8 = result.stdout_str().trim().parse().unwrap(); assert!(nproc == nproc_omp); + + // If the parsing fails, returns the number of CPU + let result = TestScenario::new(util_name!()) + .ucmd_keepenv() + .env("OMP_NUM_THREADS", "incorrectnumber") // returns the number CPU + .succeeds(); + let nproc_omp: u8 = result.stdout_str().trim().parse().unwrap(); + assert!(nproc == nproc_omp); } #[test] @@ -52,3 +61,14 @@ fn test_nproc_ignore() { assert!(nproc_total - 1 == nproc); } } + +#[test] +fn test_nproc_ignore_all_omp() { + let result = TestScenario::new(util_name!()) + .ucmd_keepenv() + .env("OMP_NUM_THREADS", "42") + .arg("--ignore=40") + .succeeds(); + let nproc: u8 = result.stdout_str().trim().parse().unwrap(); + assert!(nproc == 2); +} From 2fac6743176a05ef75ffcfae8b36d3bafd437dc0 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 12 Mar 2022 17:06:03 -0500 Subject: [PATCH 57/60] df: move Filesystem to filesystem.rs Move the `Filesystem` struct into a new module `filesystem.rs`. --- src/uu/df/src/df.rs | 36 +++------------------------------ src/uu/df/src/filesystem.rs | 40 +++++++++++++++++++++++++++++++++++++ src/uu/df/src/table.rs | 3 ++- 3 files changed, 45 insertions(+), 34 deletions(-) create mode 100644 src/uu/df/src/filesystem.rs diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 854bc2a48..b877faa72 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -8,13 +8,12 @@ // spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs lofs mod blocks; mod columns; +mod filesystem; mod table; use uucore::error::{UResult, USimpleError}; use uucore::format_usage; -#[cfg(unix)] -use uucore::fsext::statfs; -use uucore::fsext::{read_fs_list, FsUsage, MountInfo}; +use uucore::fsext::{read_fs_list, MountInfo}; use clap::{crate_version, App, AppSettings, Arg, ArgMatches}; @@ -23,6 +22,7 @@ use std::path::Path; use crate::blocks::{block_size_from_matches, BlockSize}; use crate::columns::Column; +use crate::filesystem::Filesystem; use crate::table::{DisplayRow, Header, Row}; static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\ @@ -136,36 +136,6 @@ impl Options { } } -#[derive(Debug, Clone)] -struct Filesystem { - mount_info: MountInfo, - usage: FsUsage, -} - -impl Filesystem { - // TODO: resolve uuid in `mount_info.dev_name` if exists - fn new(mount_info: MountInfo) -> Option { - let _stat_path = if !mount_info.mount_dir.is_empty() { - mount_info.mount_dir.clone() - } else { - #[cfg(unix)] - { - mount_info.dev_name.clone() - } - #[cfg(windows)] - { - // On windows, we expect the volume id - mount_info.dev_id.clone() - } - }; - #[cfg(unix)] - let usage = FsUsage::new(statfs(_stat_path).ok()?); - #[cfg(windows)] - let usage = FsUsage::new(Path::new(&_stat_path)); - Some(Self { mount_info, usage }) - } -} - /// Whether to display the mount info given the inclusion settings. fn is_included(mi: &MountInfo, opt: &Options) -> bool { // Don't show remote filesystems if `--local` has been given. diff --git a/src/uu/df/src/filesystem.rs b/src/uu/df/src/filesystem.rs new file mode 100644 index 000000000..fac85500c --- /dev/null +++ b/src/uu/df/src/filesystem.rs @@ -0,0 +1,40 @@ +// * This file is part of the uutils coreutils package. +// * +// * For the full copyright and license information, please view the LICENSE +// * file that was distributed with this source code. +#[cfg(windows)] +use std::path::Path; + +#[cfg(unix)] +use uucore::fsext::statfs; +use uucore::fsext::{FsUsage, MountInfo}; + +#[derive(Debug, Clone)] +pub(crate) struct Filesystem { + pub mount_info: MountInfo, + pub usage: FsUsage, +} + +impl Filesystem { + // TODO: resolve uuid in `mount_info.dev_name` if exists + pub(crate) fn new(mount_info: MountInfo) -> Option { + let _stat_path = if !mount_info.mount_dir.is_empty() { + mount_info.mount_dir.clone() + } else { + #[cfg(unix)] + { + mount_info.dev_name.clone() + } + #[cfg(windows)] + { + // On windows, we expect the volume id + mount_info.dev_id.clone() + } + }; + #[cfg(unix)] + let usage = FsUsage::new(statfs(_stat_path).ok()?); + #[cfg(windows)] + let usage = FsUsage::new(Path::new(&_stat_path)); + Some(Self { mount_info, usage }) + } +} diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 1e59c4ad6..a126876dd 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -12,7 +12,8 @@ use number_prefix::NumberPrefix; use crate::columns::Column; -use crate::{BlockSize, Filesystem, Options}; +use crate::filesystem::Filesystem; +use crate::{BlockSize, Options}; use uucore::fsext::{FsUsage, MountInfo}; use std::fmt; From b81f4ef6a7eeae65dea178c7d8be28f3a3af989c Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 12 Mar 2022 17:06:41 -0500 Subject: [PATCH 58/60] df: add documentation comments for filesystem.rs --- src/uu/df/src/filesystem.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/uu/df/src/filesystem.rs b/src/uu/df/src/filesystem.rs index fac85500c..abea48fad 100644 --- a/src/uu/df/src/filesystem.rs +++ b/src/uu/df/src/filesystem.rs @@ -2,6 +2,11 @@ // * // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. +//! Provides a summary representation of a filesystem. +//! +//! A [`Filesystem`] struct represents a device containing a +//! filesystem mounted at a particular directory. It also includes +//! information on amount of space available and amount of space used. #[cfg(windows)] use std::path::Path; @@ -9,9 +14,19 @@ use std::path::Path; use uucore::fsext::statfs; use uucore::fsext::{FsUsage, MountInfo}; +/// Summary representation of a filesystem. +/// +/// A [`Filesystem`] struct represents a device containing a +/// filesystem mounted at a particular directory. The +/// [`Filesystem::mount_info`] field exposes that information. The +/// [`Filesystem::usage`] field provides information on the amount of +/// space available on the filesystem and the amount of space used. #[derive(Debug, Clone)] pub(crate) struct Filesystem { + /// Information about the mounted device, mount directory, and related options. pub mount_info: MountInfo, + + /// Information about the amount of space used on the filesystem. pub usage: FsUsage, } From 0571434a752d5f8b4f7033488db0820fa28d61a6 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 13 Mar 2022 21:03:40 +0100 Subject: [PATCH 59/60] ci: rename of the tracking size field to match the tracking project --- .github/workflows/CICD.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index ecb1d8d6f..9ca47e613 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -5,7 +5,7 @@ name: CICD # spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain # spell-checker:ignore (names) CodeCOV MacOS MinGW Peltoche rivy # spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rsync rustc rustfmt rustup shopt xargs -# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend pell runtest tempfile testsuite uutils DESTDIR sizemulti Swatinem +# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend pell runtest tempfile testsuite uutils DESTDIR multisize Swatinem # ToDO: [2021-06; rivy] change from `cargo-tree` to `cargo tree` once MSRV is >= 1.45 @@ -525,8 +525,8 @@ jobs: --arg date "$(date --rfc-email)" \ --arg sha "$GITHUB_SHA" \ --arg size "$SIZE" \ - --arg sizemulti "$SIZEMULTI" \ - '{($date): { sha: $sha, size: $size, sizemulti: $sizemulti, }}' > size-result.json + --arg multisize "$SIZEMULTI" \ + '{($date): { sha: $sha, size: $size, multisize: $multisize, }}' > size-result.json - uses: actions/upload-artifact@v2 with: name: size-result From 2dc3d867d8d12acae08874c654c1d3bebc149a0b Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Tue, 8 Feb 2022 19:37:55 -0600 Subject: [PATCH 60/60] cp: Avoid following a destination symlink with -P Previously, given 'cp -P a b', where 'a' and 'b' were both symlinks, cp would end up replacing the target of 'b'. Signed-off-by: Ryan Gonzalez --- src/uu/cp/src/cp.rs | 68 +++++++++++++++++++++++++++++++--------- tests/by-util/test_cp.rs | 46 +++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 15 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 63dca874e..a4a512d6b 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1245,7 +1245,8 @@ fn handle_existing_dest(source: &Path, dest: &Path, options: &Options) -> CopyRe } /// Copy the a file from `source` to `dest`. `source` will be dereferenced if -/// `options.dereference` is set to true. `dest` will always be dereferenced. +/// `options.dereference` is set to true. `dest` will be dereferenced only if +/// the source was not a symlink. /// /// Behavior when copying to existing files is contingent on the /// `options.overwrite` mode. If a file is skipped, the return type @@ -1295,13 +1296,32 @@ fn copy_file( let context = context.as_str(); // canonicalize dest and source so that later steps can work with the paths directly - let dest = canonicalize(dest, MissingHandling::Missing, ResolveMode::Physical).unwrap(); let source = if options.dereference { canonicalize(source, MissingHandling::Missing, ResolveMode::Physical).unwrap() } else { source.to_owned() }; + let source_file_type = fs::symlink_metadata(&source).context(context)?.file_type(); + let source_is_symlink = source_file_type.is_symlink(); + + #[cfg(unix)] + let source_is_fifo = source_file_type.is_fifo(); + #[cfg(not(unix))] + let source_is_fifo = false; + + let dest_already_exists_as_symlink = fs::symlink_metadata(&dest) + .map(|meta| meta.file_type().is_symlink()) + .unwrap_or(false); + + let dest = if !(source_is_symlink && dest_already_exists_as_symlink) { + canonicalize(dest, MissingHandling::Missing, ResolveMode::Physical).unwrap() + } else { + // Don't canonicalize a symlink copied over another symlink, because + // then we'll end up overwriting the destination's target. + dest.to_path_buf() + }; + let dest_permissions = if dest.exists() { dest.symlink_metadata().context(context)?.permissions() } else { @@ -1339,7 +1359,15 @@ fn copy_file( fs::hard_link(&source, &dest).context(context)?; } CopyMode::Copy => { - copy_helper(&source, &dest, options, context, symlinked_files)?; + copy_helper( + &source, + &dest, + options, + context, + source_is_symlink, + source_is_fifo, + symlinked_files, + )?; } CopyMode::SymLink => { symlink_file(&source, &dest, context, symlinked_files)?; @@ -1355,10 +1383,26 @@ fn copy_file( if src_time <= dest_time { return Ok(()); } else { - copy_helper(&source, &dest, options, context, symlinked_files)?; + copy_helper( + &source, + &dest, + options, + context, + source_is_symlink, + source_is_fifo, + symlinked_files, + )?; } } else { - copy_helper(&source, &dest, options, context, symlinked_files)?; + copy_helper( + &source, + &dest, + options, + context, + source_is_symlink, + source_is_fifo, + symlinked_files, + )?; } } CopyMode::AttrOnly => { @@ -1397,6 +1441,8 @@ fn copy_helper( dest: &Path, options: &Options, context: &str, + source_is_symlink: bool, + source_is_fifo: bool, symlinked_files: &mut HashSet, ) -> CopyResult<()> { if options.parents { @@ -1404,23 +1450,15 @@ fn copy_helper( fs::create_dir_all(parent)?; } - let file_type = fs::symlink_metadata(&source)?.file_type(); - let is_symlink = file_type.is_symlink(); - - #[cfg(unix)] - let is_fifo = file_type.is_fifo(); - #[cfg(not(unix))] - let is_fifo = false; - if source.as_os_str() == "/dev/null" { /* workaround a limitation of fs::copy * https://github.com/rust-lang/rust/issues/79390 */ File::create(dest).context(dest.display().to_string())?; - } else if is_fifo && options.recursive { + } else if source_is_fifo && options.recursive { #[cfg(unix)] copy_fifo(dest, options.overwrite)?; - } else if is_symlink { + } else if source_is_symlink { copy_link(source, dest, symlinked_files)?; } else if options.reflink_mode != ReflinkMode::Never { #[cfg(not(any(target_os = "linux", target_os = "macos")))] diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 7ba2fa762..9ee76bb5e 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -29,6 +29,7 @@ static TEST_EXISTING_FILE: &str = "existing_file.txt"; static TEST_HELLO_WORLD_SOURCE: &str = "hello_world.txt"; static TEST_HELLO_WORLD_SOURCE_SYMLINK: &str = "hello_world.txt.link"; static TEST_HELLO_WORLD_DEST: &str = "copy_of_hello_world.txt"; +static TEST_HELLO_WORLD_DEST_SYMLINK: &str = "copy_of_hello_world.txt.link"; static TEST_HOW_ARE_YOU_SOURCE: &str = "how_are_you.txt"; static TEST_HOW_ARE_YOU_DEST: &str = "hello_dir/how_are_you.txt"; static TEST_COPY_TO_FOLDER: &str = "hello_dir/"; @@ -688,6 +689,51 @@ fn test_cp_no_deref() { assert_eq!(at.read(path_to_check), "Hello, World!\n"); } +#[test] +fn test_cp_no_deref_link_onto_link() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.copy(TEST_HELLO_WORLD_SOURCE, TEST_HELLO_WORLD_DEST); + + #[cfg(not(windows))] + let _r = fs::symlink( + TEST_HELLO_WORLD_SOURCE, + at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK), + ); + #[cfg(windows)] + let _r = symlink_file( + TEST_HELLO_WORLD_SOURCE, + at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK), + ); + + #[cfg(not(windows))] + let _r = fs::symlink( + TEST_HELLO_WORLD_DEST, + at.subdir.join(TEST_HELLO_WORLD_DEST_SYMLINK), + ); + #[cfg(windows)] + let _r = symlink_file( + TEST_HELLO_WORLD_DEST, + at.subdir.join(TEST_HELLO_WORLD_DEST_SYMLINK), + ); + + ucmd.arg("-P") + .arg(TEST_HELLO_WORLD_SOURCE_SYMLINK) + .arg(TEST_HELLO_WORLD_DEST_SYMLINK) + .succeeds(); + + // Ensure that the target of the destination was not modified. + assert!(!at + .symlink_metadata(TEST_HELLO_WORLD_DEST) + .file_type() + .is_symlink()); + assert!(at + .symlink_metadata(TEST_HELLO_WORLD_DEST_SYMLINK) + .file_type() + .is_symlink()); + assert_eq!(at.read(TEST_HELLO_WORLD_DEST_SYMLINK), "Hello, World!\n"); +} + #[test] fn test_cp_strip_trailing_slashes() { let (at, mut ucmd) = at_and_ucmd!();