mirror of
https://github.com/uutils/bsdutils.git
synced 2026-06-10 16:14:07 -07:00
initial version of bsdutils - only renice for now
This commit is contained in:
+103
@@ -0,0 +1,103 @@
|
||||
# bsdutils (uutils)
|
||||
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
|
||||
|
||||
# spell-checker:ignore (libs) bigdecimal datetime fundu gethostid kqueue libselinux mangen memmap procfs uuhelp
|
||||
|
||||
[package]
|
||||
name = "bsdutils"
|
||||
version = "0.0.1"
|
||||
authors = ["uutils developers"]
|
||||
license = "MIT"
|
||||
description = "bsdutils ~ GNU bsdutils (updated); implemented as universal (cross-platform) utils, written in Rust"
|
||||
default-run = "bsdutils"
|
||||
|
||||
homepage = "https://github.com/uutils/bsdutils"
|
||||
repository = "https://github.com/uutils/bsdutils"
|
||||
readme = "README.md"
|
||||
keywords = ["bsdutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
rust-version = "1.70.0"
|
||||
edition = "2021"
|
||||
|
||||
build = "build.rs"
|
||||
|
||||
[features]
|
||||
default = ["feat_common_core"]
|
||||
|
||||
feat_common_core = [
|
||||
"renice",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
uucore = "0.0.24"
|
||||
clap = { version = "4.4", features = ["wrap_help", "cargo"] }
|
||||
clap_complete = "4.4"
|
||||
clap_mangen = "0.2"
|
||||
regex = "1.10.2"
|
||||
sysinfo = "0.30"
|
||||
libc = "0.2.152"
|
||||
phf = "0.11.2"
|
||||
phf_codegen = "0.11.2"
|
||||
textwrap = { version = "0.16.0", features = ["terminal_size"] }
|
||||
xattr = "1.3.1"
|
||||
tempfile = "3.9.0"
|
||||
rand = { version = "0.8", features = ["small_rng"] }
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
clap_complete = { workspace = true }
|
||||
clap_mangen = { workspace = true }
|
||||
uucore = { workspace = true }
|
||||
phf = { workspace = true }
|
||||
textwrap = { workspace = true }
|
||||
|
||||
|
||||
#
|
||||
renice = { optional = true, version = "0.0.1", package = "uu_renice", path = "src/uu/renice" }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1"
|
||||
regex = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
uucore = { workspace = true, features = ["entries", "process", "signals"] }
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
xattr = { workspace = true }
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
|
||||
procfs = { version = "0.16", default-features = false }
|
||||
rlimit = "0.10.1"
|
||||
|
||||
[build-dependencies]
|
||||
phf_codegen = { workspace = true }
|
||||
|
||||
|
||||
[[bin]]
|
||||
name = "bsdutils"
|
||||
path = "src/bin/bsdutils.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "uudoc"
|
||||
path = "src/bin/uudoc.rs"
|
||||
required-features = ["uudoc"]
|
||||
|
||||
# The default release profile. It contains all optimizations, without
|
||||
# sacrificing debug info. With this profile (like in the standard
|
||||
# release profile), the debug info and the stack traces will still be available.
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
# A release-like profile that is tuned to be fast, even when being fast
|
||||
# compromises on binary size. This includes aborting on panic.
|
||||
[profile.release-fast]
|
||||
inherits = "release"
|
||||
panic = "abort"
|
||||
|
||||
# A release-like profile that is as small as possible.
|
||||
[profile.release-small]
|
||||
inherits = "release"
|
||||
opt-level = "z"
|
||||
panic = "abort"
|
||||
strip = true
|
||||
@@ -0,0 +1,16 @@
|
||||
# procps
|
||||
Rust reimplemtation of the bsdutils project
|
||||
|
||||
Provides command line tools:
|
||||
|
||||
- `/usr/bin/logger`: This command is used to add logs to the system log files. It provides a shell command interface to the syslog system log module.
|
||||
|
||||
- `/usr/bin/renice`: This command is used to change the priority of running processes. It can be used to increase or decrease the nice value of a process.
|
||||
|
||||
- `/usr/bin/script`: This command is used to record a terminal session. The script makes a typescript of everything printed on your terminal.
|
||||
|
||||
- `/usr/bin/scriptlive`: This command is not standard, and may not be installed on all systems. If it exists, it's likely a variant of the `script` command that includes real-time features.
|
||||
|
||||
- `/usr/bin/scriptreplay`: This command is used to replay a terminal session that was recorded using the `script` command.
|
||||
|
||||
- `/usr/bin/wall`: This command is used to broadcast a message to all users logged into a system. The message can be typed directly into the terminal or read from a file.
|
||||
@@ -0,0 +1,101 @@
|
||||
// This file is part of the uutils bsdutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (vars) krate
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn main() {
|
||||
if let Ok(profile) = env::var("PROFILE") {
|
||||
println!("cargo:rustc-cfg=build={profile:?}");
|
||||
}
|
||||
|
||||
const ENV_FEATURE_PREFIX: &str = "CARGO_FEATURE_";
|
||||
const FEATURE_PREFIX: &str = "feat_";
|
||||
const OVERRIDE_PREFIX: &str = "uu_";
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
|
||||
let mut crates = Vec::new();
|
||||
for (key, val) in env::vars() {
|
||||
if val == "1" && key.starts_with(ENV_FEATURE_PREFIX) {
|
||||
let krate = key[ENV_FEATURE_PREFIX.len()..].to_lowercase();
|
||||
// Allow this as we have a bunch of info in the comments
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match krate.as_ref() {
|
||||
"default" | "macos" | "unix" | "windows" | "selinux" | "zip" => continue, // common/standard feature names
|
||||
"nightly" | "test_unimplemented" => continue, // crate-local custom features
|
||||
"uudoc" => continue, // is not a utility
|
||||
"test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test'
|
||||
s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets
|
||||
_ => {} // util feature name
|
||||
}
|
||||
crates.push(krate);
|
||||
}
|
||||
}
|
||||
crates.sort();
|
||||
|
||||
let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap();
|
||||
|
||||
mf.write_all(
|
||||
"type UtilityMap<T> = phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\
|
||||
\n\
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n"
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut phf_map = phf_codegen::OrderedMap::<&str>::new();
|
||||
for krate in &crates {
|
||||
let map_value = format!("({krate}::uumain, {krate}::uu_app)");
|
||||
match krate.as_ref() {
|
||||
// 'test' is named uu_test to avoid collision with rust core crate 'test'.
|
||||
// It can also be invoked by name '[' for the '[ expr ] syntax'.
|
||||
"uu_test" => {
|
||||
phf_map.entry("test", &map_value);
|
||||
phf_map.entry("[", &map_value);
|
||||
}
|
||||
k if k.starts_with(OVERRIDE_PREFIX) => {
|
||||
phf_map.entry(&k[OVERRIDE_PREFIX.len()..], &map_value);
|
||||
}
|
||||
"false" | "true" => {
|
||||
phf_map.entry(krate, &format!("(r#{krate}::uumain, r#{krate}::uu_app)"));
|
||||
}
|
||||
"hashsum" => {
|
||||
phf_map.entry(krate, &format!("({krate}::uumain, {krate}::uu_app_custom)"));
|
||||
|
||||
let map_value = format!("({krate}::uumain, {krate}::uu_app_common)");
|
||||
let map_value_bits = format!("({krate}::uumain, {krate}::uu_app_bits)");
|
||||
let map_value_b3sum = format!("({krate}::uumain, {krate}::uu_app_b3sum)");
|
||||
phf_map.entry("md5sum", &map_value);
|
||||
phf_map.entry("sha1sum", &map_value);
|
||||
phf_map.entry("sha224sum", &map_value);
|
||||
phf_map.entry("sha256sum", &map_value);
|
||||
phf_map.entry("sha384sum", &map_value);
|
||||
phf_map.entry("sha512sum", &map_value);
|
||||
phf_map.entry("sha3sum", &map_value_bits);
|
||||
phf_map.entry("sha3-224sum", &map_value);
|
||||
phf_map.entry("sha3-256sum", &map_value);
|
||||
phf_map.entry("sha3-384sum", &map_value);
|
||||
phf_map.entry("sha3-512sum", &map_value);
|
||||
phf_map.entry("shake128sum", &map_value_bits);
|
||||
phf_map.entry("shake256sum", &map_value_bits);
|
||||
phf_map.entry("b2sum", &map_value);
|
||||
phf_map.entry("b3sum", &map_value_b3sum);
|
||||
}
|
||||
_ => {
|
||||
phf_map.entry(krate, &map_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(mf, "{}", phf_map.build()).unwrap();
|
||||
mf.write_all(b"\n}\n").unwrap();
|
||||
|
||||
mf.flush().unwrap();
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
// 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 manpages mangen
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use clap_complete::Shell;
|
||||
use std::cmp;
|
||||
use std::ffi::OsStr;
|
||||
use std::ffi::OsString;
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use uucore::display::Quotable;
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
|
||||
|
||||
fn usage<T>(utils: &UtilityMap<T>, name: &str) {
|
||||
println!("{name} {VERSION} (multi-call binary)\n");
|
||||
println!("Usage: {name} [function [arguments...]]\n");
|
||||
println!("Currently defined functions:\n");
|
||||
#[allow(clippy::map_clone)]
|
||||
let mut utils: Vec<&str> = utils.keys().map(|&s| s).collect();
|
||||
utils.sort_unstable();
|
||||
let display_list = utils.join(", ");
|
||||
let width = cmp::min(textwrap::termwidth(), 100) - 4 * 2; // (opinion/heuristic) max 100 chars wide with 4 character side indentions
|
||||
println!(
|
||||
"{}",
|
||||
textwrap::indent(&textwrap::fill(&display_list, width), " ")
|
||||
);
|
||||
}
|
||||
|
||||
fn binary_path(args: &mut impl Iterator<Item = OsString>) -> PathBuf {
|
||||
match args.next() {
|
||||
Some(ref s) if !s.is_empty() => PathBuf::from(s),
|
||||
_ => std::env::current_exe().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn name(binary_path: &Path) -> Option<&str> {
|
||||
binary_path.file_stem()?.to_str()
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn main() {
|
||||
uucore::panic::mute_sigpipe_panic();
|
||||
|
||||
let utils = util_map();
|
||||
let mut args = uucore::args_os();
|
||||
|
||||
let binary = binary_path(&mut args);
|
||||
let binary_as_util = name(&binary).unwrap_or_else(|| {
|
||||
usage(&utils, "<unknown binary name>");
|
||||
process::exit(0);
|
||||
});
|
||||
|
||||
// binary name equals util name?
|
||||
if let Some(&(uumain, _)) = utils.get(binary_as_util) {
|
||||
process::exit(uumain((vec![binary.into()].into_iter()).chain(args)));
|
||||
}
|
||||
|
||||
// binary name equals prefixed util name?
|
||||
// * prefix/stem may be any string ending in a non-alphanumeric character
|
||||
let util_name = if let Some(util) = utils.keys().find(|util| {
|
||||
binary_as_util.ends_with(*util)
|
||||
&& !binary_as_util[..binary_as_util.len() - (*util).len()]
|
||||
.ends_with(char::is_alphanumeric)
|
||||
}) {
|
||||
// prefixed util => replace 0th (aka, executable name) argument
|
||||
Some(OsString::from(*util))
|
||||
} else {
|
||||
// unmatched binary name => regard as multi-binary container and advance argument list
|
||||
uucore::set_utility_is_second_arg();
|
||||
args.next()
|
||||
};
|
||||
|
||||
// 0th argument equals util name?
|
||||
if let Some(util_os) = util_name {
|
||||
fn not_found(util: &OsStr) -> ! {
|
||||
println!("{}: function/utility not found", util.maybe_quote());
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let util = match util_os.to_str() {
|
||||
Some(util) => util,
|
||||
None => not_found(&util_os),
|
||||
};
|
||||
|
||||
if util == "completion" {
|
||||
gen_completions(args, &utils);
|
||||
}
|
||||
|
||||
if util == "manpage" {
|
||||
gen_manpage(args, &utils);
|
||||
}
|
||||
|
||||
match utils.get(util) {
|
||||
Some(&(uumain, _)) => {
|
||||
process::exit(uumain((vec![util_os].into_iter()).chain(args)));
|
||||
}
|
||||
None => {
|
||||
if util == "--help" || util == "-h" {
|
||||
// see if they want help on a specific util
|
||||
if let Some(util_os) = args.next() {
|
||||
let util = match util_os.to_str() {
|
||||
Some(util) => util,
|
||||
None => not_found(&util_os),
|
||||
};
|
||||
|
||||
match utils.get(util) {
|
||||
Some(&(uumain, _)) => {
|
||||
let code = uumain(
|
||||
(vec![util_os, OsString::from("--help")].into_iter())
|
||||
.chain(args),
|
||||
);
|
||||
io::stdout().flush().expect("could not flush stdout");
|
||||
process::exit(code);
|
||||
}
|
||||
None => not_found(&util_os),
|
||||
}
|
||||
}
|
||||
usage(&utils, binary_as_util);
|
||||
process::exit(0);
|
||||
} else {
|
||||
not_found(&util_os);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no arguments provided
|
||||
usage(&utils, binary_as_util);
|
||||
process::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout
|
||||
fn gen_completions<T: uucore::Args>(
|
||||
args: impl Iterator<Item = OsString>,
|
||||
util_map: &UtilityMap<T>,
|
||||
) -> ! {
|
||||
let all_utilities: Vec<_> = std::iter::once("coreutils")
|
||||
.chain(util_map.keys().copied())
|
||||
.collect();
|
||||
|
||||
let matches = Command::new("completion")
|
||||
.about("Prints completions to stdout")
|
||||
.arg(
|
||||
Arg::new("utility")
|
||||
.value_parser(clap::builder::PossibleValuesParser::new(all_utilities))
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("shell")
|
||||
.value_parser(clap::builder::EnumValueParser::<Shell>::new())
|
||||
.required(true),
|
||||
)
|
||||
.get_matches_from(std::iter::once(OsString::from("completion")).chain(args));
|
||||
|
||||
let utility = matches.get_one::<String>("utility").unwrap();
|
||||
let shell = *matches.get_one::<Shell>("shell").unwrap();
|
||||
|
||||
let mut command = if utility == "coreutils" {
|
||||
gen_coreutils_app(util_map)
|
||||
} else {
|
||||
util_map.get(utility).unwrap().1()
|
||||
};
|
||||
let bin_name = std::env::var("PROG_PREFIX").unwrap_or_default() + utility;
|
||||
|
||||
clap_complete::generate(shell, &mut command, bin_name, &mut io::stdout());
|
||||
io::stdout().flush().unwrap();
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
/// Generate the manpage for the utility in the first parameter
|
||||
fn gen_manpage<T: uucore::Args>(
|
||||
args: impl Iterator<Item = OsString>,
|
||||
util_map: &UtilityMap<T>,
|
||||
) -> ! {
|
||||
let all_utilities: Vec<_> = std::iter::once("coreutils")
|
||||
.chain(util_map.keys().copied())
|
||||
.collect();
|
||||
|
||||
let matches = Command::new("manpage")
|
||||
.about("Prints manpage to stdout")
|
||||
.arg(
|
||||
Arg::new("utility")
|
||||
.value_parser(clap::builder::PossibleValuesParser::new(all_utilities))
|
||||
.required(true),
|
||||
)
|
||||
.get_matches_from(std::iter::once(OsString::from("manpage")).chain(args));
|
||||
|
||||
let utility = matches.get_one::<String>("utility").unwrap();
|
||||
|
||||
let command = if utility == "coreutils" {
|
||||
gen_coreutils_app(util_map)
|
||||
} else {
|
||||
util_map.get(utility).unwrap().1()
|
||||
};
|
||||
|
||||
let man = clap_mangen::Man::new(command);
|
||||
man.render(&mut io::stdout())
|
||||
.expect("Man page generation failed");
|
||||
io::stdout().flush().unwrap();
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
fn gen_coreutils_app<T: uucore::Args>(util_map: &UtilityMap<T>) -> Command {
|
||||
let mut command = Command::new("coreutils");
|
||||
for (name, (_, sub_app)) in util_map {
|
||||
// Recreate a small subcommand with only the relevant info
|
||||
// (name & short description)
|
||||
let about = sub_app()
|
||||
.get_about()
|
||||
.expect("Could not get the 'about'")
|
||||
.to_string();
|
||||
let sub_app = Command::new(name).about(about);
|
||||
command = command.subcommand(sub_app);
|
||||
}
|
||||
command
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
// 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 tldr uuhelp
|
||||
|
||||
use clap::Command;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Seek, Write};
|
||||
use zip::ZipArchive;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let mut tldr_zip = File::open("docs/tldr.zip")
|
||||
.ok()
|
||||
.and_then(|f| ZipArchive::new(f).ok());
|
||||
|
||||
if tldr_zip.is_none() {
|
||||
println!("Warning: No tldr archive found, so the documentation will not include examples.");
|
||||
println!("To include examples in the documentation, download the tldr archive and put it in the docs/ folder.");
|
||||
println!();
|
||||
println!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip");
|
||||
println!();
|
||||
}
|
||||
|
||||
let utils = util_map::<Box<dyn Iterator<Item = OsString>>>();
|
||||
match std::fs::create_dir("docs/src/utils/") {
|
||||
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()),
|
||||
x => x,
|
||||
}?;
|
||||
|
||||
println!("Writing initial info to SUMMARY.md");
|
||||
let mut summary = File::create("docs/src/SUMMARY.md")?;
|
||||
|
||||
let _ = write!(
|
||||
summary,
|
||||
"# Summary\n\
|
||||
\n\
|
||||
[Introduction](index.md)\n\
|
||||
* [Installation](installation.md)\n\
|
||||
* [Build from source](build.md)\n\
|
||||
* [Platform support](platforms.md)\n\
|
||||
* [Contributing](contributing.md)\n\
|
||||
* [GNU test coverage](test_coverage.md)\n\
|
||||
* [Extensions](extensions.md)\n\
|
||||
\n\
|
||||
# Reference\n\
|
||||
* [Multi-call binary](multicall.md)\n",
|
||||
);
|
||||
|
||||
println!("Gathering utils per platform");
|
||||
let utils_per_platform = {
|
||||
let mut map = HashMap::new();
|
||||
for platform in ["unix", "macos", "windows", "unix_android"] {
|
||||
let platform_utils: Vec<String> = String::from_utf8(
|
||||
std::process::Command::new("./util/show-utils.sh")
|
||||
.arg(format!("--features=feat_os_{}", platform))
|
||||
.output()?
|
||||
.stdout,
|
||||
)
|
||||
.unwrap()
|
||||
.trim()
|
||||
.split(' ')
|
||||
.map(ToString::to_string)
|
||||
.collect();
|
||||
map.insert(platform, platform_utils);
|
||||
}
|
||||
|
||||
// Linux is a special case because it can support selinux
|
||||
let platform_utils: Vec<String> = String::from_utf8(
|
||||
std::process::Command::new("./util/show-utils.sh")
|
||||
.arg("--features=feat_os_unix feat_selinux")
|
||||
.output()?
|
||||
.stdout,
|
||||
)
|
||||
.unwrap()
|
||||
.trim()
|
||||
.split(' ')
|
||||
.map(ToString::to_string)
|
||||
.collect();
|
||||
map.insert("linux", platform_utils);
|
||||
|
||||
map
|
||||
};
|
||||
|
||||
let mut utils = utils.entries().collect::<Vec<_>>();
|
||||
utils.sort();
|
||||
|
||||
println!("Writing util per platform table");
|
||||
{
|
||||
let mut platform_table_file = File::create("docs/src/platform_table.md").unwrap();
|
||||
|
||||
// sum, cksum, b2sum, etc. are all available on all platforms, but not in the data structure
|
||||
// otherwise, we check the map for the util name.
|
||||
let check_supported = |name: &str, platform: &str| {
|
||||
if name.ends_with("sum") || utils_per_platform[platform].iter().any(|u| u == name) {
|
||||
"✓"
|
||||
} else {
|
||||
" "
|
||||
}
|
||||
};
|
||||
writeln!(
|
||||
platform_table_file,
|
||||
"| util | Linux | macOS | Windows | FreeBSD | Android |\n\
|
||||
| ---------------- | ----- | ----- | ------- | ------- | ------- |"
|
||||
)?;
|
||||
for (&name, _) in &utils {
|
||||
if name == "[" {
|
||||
continue;
|
||||
}
|
||||
// The alignment is not necessary, but makes the output a bit more
|
||||
// pretty when viewed as plain markdown.
|
||||
writeln!(
|
||||
platform_table_file,
|
||||
"| {:<16} | {:<5} | {:<5} | {:<7} | {:<7} | {:<7} |",
|
||||
format!("**{name}**"),
|
||||
check_supported(name, "linux"),
|
||||
check_supported(name, "macos"),
|
||||
check_supported(name, "windows"),
|
||||
check_supported(name, "unix"),
|
||||
check_supported(name, "unix_android"),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
println!("Writing to utils");
|
||||
for (&name, (_, command)) in utils {
|
||||
if name == "[" {
|
||||
continue;
|
||||
}
|
||||
let p = format!("docs/src/utils/{}.md", name);
|
||||
|
||||
let markdown = File::open(format!("src/uu/{name}/{name}.md"))
|
||||
.and_then(|mut f: File| {
|
||||
let mut s = String::new();
|
||||
f.read_to_string(&mut s)?;
|
||||
Ok(s)
|
||||
})
|
||||
.ok();
|
||||
|
||||
if let Ok(f) = File::create(&p) {
|
||||
MDWriter {
|
||||
w: Box::new(f),
|
||||
command: command(),
|
||||
name,
|
||||
tldr_zip: &mut tldr_zip,
|
||||
utils_per_platform: &utils_per_platform,
|
||||
markdown,
|
||||
}
|
||||
.markdown()?;
|
||||
println!("Wrote to '{}'", p);
|
||||
} else {
|
||||
println!("Error writing to {}", p);
|
||||
}
|
||||
writeln!(summary, "* [{0}](utils/{0}.md)", name)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct MDWriter<'a, 'b> {
|
||||
w: Box<dyn Write>,
|
||||
command: Command,
|
||||
name: &'a str,
|
||||
tldr_zip: &'b mut Option<ZipArchive<File>>,
|
||||
utils_per_platform: &'b HashMap<&'b str, Vec<String>>,
|
||||
markdown: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> MDWriter<'a, 'b> {
|
||||
fn markdown(&mut self) -> io::Result<()> {
|
||||
write!(self.w, "# {}\n\n", self.name)?;
|
||||
self.additional()?;
|
||||
self.usage()?;
|
||||
self.about()?;
|
||||
self.options()?;
|
||||
self.after_help()?;
|
||||
self.examples()
|
||||
}
|
||||
|
||||
fn additional(&mut self) -> io::Result<()> {
|
||||
writeln!(self.w, "<div class=\"additional\">")?;
|
||||
self.platforms()?;
|
||||
self.version()?;
|
||||
writeln!(self.w, "</div>")
|
||||
}
|
||||
|
||||
fn platforms(&mut self) -> io::Result<()> {
|
||||
writeln!(self.w, "<div class=\"platforms\">")?;
|
||||
for (feature, icon) in [
|
||||
("linux", "linux"),
|
||||
// freebsd is disabled for now because mdbook does not use font-awesome 5 yet.
|
||||
// ("unix", "freebsd"),
|
||||
("macos", "apple"),
|
||||
("windows", "windows"),
|
||||
] {
|
||||
if self.name.contains("sum")
|
||||
|| self.utils_per_platform[feature]
|
||||
.iter()
|
||||
.any(|u| u == self.name)
|
||||
{
|
||||
writeln!(self.w, "<i class=\"fa fa-brands fa-{}\"></i>", icon)?;
|
||||
}
|
||||
}
|
||||
writeln!(self.w, "</div>")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn version(&mut self) -> io::Result<()> {
|
||||
writeln!(
|
||||
self.w,
|
||||
"<div class=\"version\">v{}</div>",
|
||||
self.command.render_version().split_once(' ').unwrap().1
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&mut self) -> io::Result<()> {
|
||||
if let Some(markdown) = &self.markdown {
|
||||
let usage = uuhelp_parser::parse_usage(markdown);
|
||||
let usage = usage.replace("{}", self.name);
|
||||
|
||||
writeln!(self.w, "\n```")?;
|
||||
writeln!(self.w, "{}", usage)?;
|
||||
writeln!(self.w, "```")
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn about(&mut self) -> io::Result<()> {
|
||||
if let Some(markdown) = &self.markdown {
|
||||
writeln!(self.w, "{}", uuhelp_parser::parse_about(markdown))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn after_help(&mut self) -> io::Result<()> {
|
||||
if let Some(markdown) = &self.markdown {
|
||||
if let Some(after_help) = uuhelp_parser::parse_section("after help", markdown) {
|
||||
return writeln!(self.w, "\n\n{after_help}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn examples(&mut self) -> io::Result<()> {
|
||||
if let Some(zip) = self.tldr_zip {
|
||||
let content = if let Some(f) =
|
||||
get_zip_content(zip, &format!("pages/common/{}.md", self.name))
|
||||
{
|
||||
f
|
||||
} else if let Some(f) = get_zip_content(zip, &format!("pages/linux/{}.md", self.name)) {
|
||||
f
|
||||
} else {
|
||||
println!(
|
||||
"Warning: Could not find tldr examples for page '{}'",
|
||||
self.name
|
||||
);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
writeln!(self.w, "## Examples")?;
|
||||
writeln!(self.w)?;
|
||||
for line in content.lines().skip_while(|l| !l.starts_with('-')) {
|
||||
if let Some(l) = line.strip_prefix("- ") {
|
||||
writeln!(self.w, "{}", l)?;
|
||||
} else if line.starts_with('`') {
|
||||
writeln!(self.w, "```shell\n{}\n```", line.trim_matches('`'))?;
|
||||
} else if line.is_empty() {
|
||||
writeln!(self.w)?;
|
||||
} else {
|
||||
println!("Not sure what to do with this line:");
|
||||
println!("{}", line);
|
||||
}
|
||||
}
|
||||
writeln!(self.w)?;
|
||||
writeln!(
|
||||
self.w,
|
||||
"> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)."
|
||||
)?;
|
||||
writeln!(self.w, ">")?;
|
||||
writeln!(
|
||||
self.w,
|
||||
"> Please note that, as uutils is a work in progress, some examples might fail."
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn options(&mut self) -> io::Result<()> {
|
||||
writeln!(self.w, "<h2>Options</h2>")?;
|
||||
write!(self.w, "<dl>")?;
|
||||
for arg in self.command.get_arguments() {
|
||||
write!(self.w, "<dt>")?;
|
||||
let mut first = true;
|
||||
for l in arg.get_long_and_visible_aliases().unwrap_or_default() {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
write!(self.w, ", ")?;
|
||||
}
|
||||
write!(self.w, "<code>")?;
|
||||
write!(self.w, "--{}", l)?;
|
||||
if let Some(names) = arg.get_value_names() {
|
||||
write!(
|
||||
self.w,
|
||||
"={}",
|
||||
names
|
||||
.iter()
|
||||
.map(|x| format!("<{}>", x))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
)?;
|
||||
}
|
||||
write!(self.w, "</code>")?;
|
||||
}
|
||||
for s in arg.get_short_and_visible_aliases().unwrap_or_default() {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
write!(self.w, ", ")?;
|
||||
}
|
||||
write!(self.w, "<code>")?;
|
||||
write!(self.w, "-{}", s)?;
|
||||
if let Some(names) = arg.get_value_names() {
|
||||
write!(
|
||||
self.w,
|
||||
" {}",
|
||||
names
|
||||
.iter()
|
||||
.map(|x| format!("<{}>", x))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
)?;
|
||||
}
|
||||
write!(self.w, "</code>")?;
|
||||
}
|
||||
writeln!(self.w, "</dt>")?;
|
||||
writeln!(
|
||||
self.w,
|
||||
"<dd>\n\n{}\n\n</dd>",
|
||||
arg.get_help()
|
||||
.unwrap_or_default()
|
||||
.to_string()
|
||||
.replace('\n', "<br />")
|
||||
)?;
|
||||
}
|
||||
writeln!(self.w, "</dl>\n")
|
||||
}
|
||||
}
|
||||
|
||||
fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Option<String> {
|
||||
let mut s = String::new();
|
||||
archive.by_name(name).ok()?.read_to_string(&mut s).unwrap();
|
||||
Some(s)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "uu_renice"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc = { workspace = true }
|
||||
uucore = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
|
||||
[lib]
|
||||
path = "src/renice.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "renice"
|
||||
path = "src/main.rs"
|
||||
@@ -0,0 +1,7 @@
|
||||
# renice
|
||||
|
||||
```
|
||||
renice [--priority|--relative] priority [-g|-p|-u] identifier...
|
||||
```
|
||||
|
||||
Alter priority of running processes
|
||||
@@ -0,0 +1 @@
|
||||
uucore::bin!(uu_renice);
|
||||
@@ -0,0 +1,60 @@
|
||||
// This file is part of the uutils bsdutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use libc::PRIO_PROCESS;
|
||||
use std::env;
|
||||
use std::io::Error;
|
||||
use std::process;
|
||||
use uucore::{error::UResult, format_usage, help_about, help_usage};
|
||||
const ABOUT: &str = help_about!("renice.md");
|
||||
const USAGE: &str = help_usage!("renice.md");
|
||||
use clap::{crate_version, Arg, Command};
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
|
||||
let nice_value_str = matches.get_one::<String>("nice_value").unwrap(); // Retrieve as String
|
||||
let nice_value = nice_value_str.parse::<i32>().unwrap_or_else(|_| {
|
||||
eprintln!("Invalid nice value");
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
let pid_str = matches.get_one::<String>("pid").unwrap(); // Retrieve as String
|
||||
let pid = pid_str.parse::<i32>().unwrap_or_else(|_| {
|
||||
eprintln!("Invalid PID");
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
if unsafe { libc::setpriority(PRIO_PROCESS, pid.try_into().unwrap(), nice_value) } == -1 {
|
||||
eprintln!("Failed to set nice value: {}", Error::last_os_error());
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
println!("Nice value of process {} set to {}", pid, nice_value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
.override_usage(format_usage(USAGE))
|
||||
.infer_long_args(true)
|
||||
.arg(
|
||||
Arg::new("nice_value")
|
||||
.value_name("NICE_VALUE")
|
||||
.help("The new nice value for the process")
|
||||
.required(true)
|
||||
.index(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("pid")
|
||||
.value_name("PID")
|
||||
.help("The PID of the process")
|
||||
.required(true)
|
||||
.index(2),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// This file is part of the uutils bsdutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
// spell-checker:ignore (words) symdir somefakedir
|
||||
|
||||
use crate::common::util::{TestScenario, UCommand};
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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.
|
||||
|
||||
/// Platform-independent helper for constructing a `PathBuf` from individual elements
|
||||
#[macro_export]
|
||||
macro_rules! path_concat {
|
||||
($e:expr, ..$n:expr) => {{
|
||||
use std::path::PathBuf;
|
||||
let n = $n;
|
||||
let mut pb = PathBuf::new();
|
||||
for _ in 0..n {
|
||||
pb.push($e);
|
||||
}
|
||||
pb.to_str().unwrap().to_owned()
|
||||
}};
|
||||
($($e:expr),*) => {{
|
||||
use std::path::PathBuf;
|
||||
let mut pb = PathBuf::new();
|
||||
$(
|
||||
pb.push($e);
|
||||
)*
|
||||
pb.to_str().unwrap().to_owned()
|
||||
}};
|
||||
}
|
||||
|
||||
/// Deduce the name of the test binary from the test filename.
|
||||
///
|
||||
/// e.g.: `tests/by-util/test_cat.rs` -> `cat`
|
||||
#[macro_export]
|
||||
macro_rules! util_name {
|
||||
() => {
|
||||
module_path!()
|
||||
.split("_")
|
||||
.nth(1)
|
||||
.and_then(|s| s.split("::").next())
|
||||
.expect("no test name")
|
||||
};
|
||||
}
|
||||
|
||||
/// Convenience macro for acquiring a [`UCommand`] builder.
|
||||
///
|
||||
/// Returns the following:
|
||||
/// - a [`UCommand`] builder for invoking the binary to be tested
|
||||
///
|
||||
/// This macro is intended for quick, single-call tests. For more complex tests
|
||||
/// that require multiple invocations of the tested binary, see [`TestScenario`]
|
||||
///
|
||||
/// [`UCommand`]: crate::tests::common::util::UCommand
|
||||
/// [`TestScenario]: crate::tests::common::util::TestScenario
|
||||
#[macro_export]
|
||||
macro_rules! new_ucmd {
|
||||
() => {
|
||||
TestScenario::new(util_name!()).ucmd()
|
||||
};
|
||||
}
|
||||
|
||||
/// Convenience macro for acquiring a [`UCommand`] builder and a test path.
|
||||
///
|
||||
/// Returns a tuple containing the following:
|
||||
/// - an [`AtPath`] that points to a unique temporary test directory
|
||||
/// - a [`UCommand`] builder for invoking the binary to be tested
|
||||
///
|
||||
/// This macro is intended for quick, single-call tests. For more complex tests
|
||||
/// that require multiple invocations of the tested binary, see [`TestScenario`]
|
||||
///
|
||||
/// [`UCommand`]: crate::tests::common::util::UCommand
|
||||
/// [`AtPath`]: crate::tests::common::util::AtPath
|
||||
/// [`TestScenario]: crate::tests::common::util::TestScenario
|
||||
#[macro_export]
|
||||
macro_rules! at_and_ucmd {
|
||||
() => {{
|
||||
let ts = TestScenario::new(util_name!());
|
||||
(ts.fixtures.clone(), ts.ucmd())
|
||||
}};
|
||||
}
|
||||
|
||||
/// If `common::util::expected_result` returns an error, i.e. the `util` in `$PATH` doesn't
|
||||
/// include a coreutils version string or the version is too low,
|
||||
/// this macro can be used to automatically skip the test and print the reason.
|
||||
#[macro_export]
|
||||
macro_rules! unwrap_or_return {
|
||||
( $e:expr ) => {
|
||||
match $e {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
println!("test skipped: {}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// 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.
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub mod random;
|
||||
pub mod util;
|
||||
@@ -0,0 +1,339 @@
|
||||
// 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.
|
||||
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
/// Samples alphanumeric characters `[A-Za-z0-9]` including newline `\n`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use rand::{Rng, thread_rng};
|
||||
///
|
||||
/// let vec = thread_rng()
|
||||
/// .sample_iter(AlphanumericNewline)
|
||||
/// .take(10)
|
||||
/// .collect::<Vec<u8>>();
|
||||
/// println!("Random chars: {}", String::from_utf8(vec).unwrap());
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct AlphanumericNewline;
|
||||
|
||||
impl AlphanumericNewline {
|
||||
/// The charset to act upon
|
||||
const CHARSET: &'static [u8] =
|
||||
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n";
|
||||
|
||||
/// Generate a random byte from [`Self::CHARSET`] and return it as `u8`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `rng`: A [`rand::Rng`]
|
||||
///
|
||||
/// returns: u8
|
||||
fn random<R>(rng: &mut R) -> u8
|
||||
where
|
||||
R: Rng + ?Sized,
|
||||
{
|
||||
let idx = rng.gen_range(0..Self::CHARSET.len());
|
||||
Self::CHARSET[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl Distribution<u8> for AlphanumericNewline {
|
||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u8 {
|
||||
Self::random(rng)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a random string from a [`Distribution`]
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use crate::common::random::{AlphanumericNewline, RandomString};
|
||||
/// use rand::distributions::Alphanumeric;
|
||||
///
|
||||
/// // generates a 100 byte string with characters from AlphanumericNewline
|
||||
/// let random_string = RandomString::generate(AlphanumericNewline, 100);
|
||||
/// assert_eq!(100, random_string.len());
|
||||
///
|
||||
/// // generates a 100 byte string with 10 newline characters not ending with a newline
|
||||
/// let string = RandomString::generate_with_delimiter(Alphanumeric, b'\n', 10, false, 100);
|
||||
/// assert_eq!(100, random_string.len());
|
||||
/// ```
|
||||
pub struct RandomString;
|
||||
|
||||
impl RandomString {
|
||||
/// Generate a random string from the given [`Distribution`] with the given `length` in bytes.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `dist`: A u8 [`Distribution`]
|
||||
/// * `length`: the length of the resulting string in bytes
|
||||
///
|
||||
/// returns: String
|
||||
pub fn generate<D>(dist: D, length: usize) -> String
|
||||
where
|
||||
D: Distribution<u8>,
|
||||
{
|
||||
thread_rng()
|
||||
.sample_iter(dist)
|
||||
.take(length)
|
||||
.map(|b| b as char)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Generate a random string from the [`Distribution`] with the given `length` in bytes. The
|
||||
/// function takes a `delimiter`, which is randomly distributed in the string, such that exactly
|
||||
/// `num_delimiter` amount of `delimiter`s occur. If `end_with_delimiter` is set, then the
|
||||
/// string ends with the delimiter, else the string does not end with the delimiter.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `dist`: A `u8` [`Distribution`]
|
||||
/// * `delimiter`: A `u8` delimiter, which does not need to be included in the `Distribution`
|
||||
/// * `num_delimiter`: The number of `delimiter`s contained in the resulting string
|
||||
/// * `end_with_delimiter`: If the string shall end with the given delimiter
|
||||
/// * `length`: the length of the resulting string in bytes
|
||||
///
|
||||
/// returns: String
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use crate::common::random::{AlphanumericNewline, RandomString};
|
||||
///
|
||||
/// // generates a 100 byte string with 10 '\0' byte characters not ending with a '\0' byte
|
||||
/// let string = RandomString::generate_with_delimiter(AlphanumericNewline, 0, 10, false, 100);
|
||||
/// assert_eq!(100, random_string.len());
|
||||
/// assert_eq!(
|
||||
/// 10,
|
||||
/// random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
/// );
|
||||
/// assert!(!random_string.as_bytes().ends_with(&[0]));
|
||||
/// ```
|
||||
pub fn generate_with_delimiter<D>(
|
||||
dist: D,
|
||||
delimiter: u8,
|
||||
num_delimiter: usize,
|
||||
end_with_delimiter: bool,
|
||||
length: usize,
|
||||
) -> String
|
||||
where
|
||||
D: Distribution<u8>,
|
||||
{
|
||||
if length == 0 {
|
||||
return String::new();
|
||||
} else if length == 1 {
|
||||
return if num_delimiter > 0 {
|
||||
String::from(delimiter as char)
|
||||
} else {
|
||||
String::from(thread_rng().sample(&dist) as char)
|
||||
};
|
||||
}
|
||||
|
||||
let samples = length - 1;
|
||||
let mut result: Vec<u8> = thread_rng().sample_iter(&dist).take(samples).collect();
|
||||
|
||||
if num_delimiter == 0 {
|
||||
result.push(thread_rng().sample(&dist));
|
||||
return String::from_utf8(result).unwrap();
|
||||
}
|
||||
|
||||
let num_delimiter = if end_with_delimiter {
|
||||
num_delimiter - 1
|
||||
} else {
|
||||
num_delimiter
|
||||
};
|
||||
|
||||
let between = Uniform::new(0, samples);
|
||||
for _ in 0..num_delimiter {
|
||||
let mut pos = between.sample(&mut thread_rng());
|
||||
let turn = pos;
|
||||
while result[pos] == delimiter {
|
||||
pos += 1;
|
||||
if pos >= samples {
|
||||
pos = 0;
|
||||
}
|
||||
if pos == turn {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result[pos] = delimiter;
|
||||
}
|
||||
|
||||
if end_with_delimiter {
|
||||
result.push(delimiter);
|
||||
} else {
|
||||
result.push(thread_rng().sample(&dist));
|
||||
}
|
||||
|
||||
String::from_utf8(result).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rand::distributions::Alphanumeric;
|
||||
|
||||
#[test]
|
||||
fn test_random_string_generate() {
|
||||
let random_string = RandomString::generate(AlphanumericNewline, 0);
|
||||
assert_eq!(0, random_string.len());
|
||||
|
||||
let random_string = RandomString::generate(AlphanumericNewline, 1);
|
||||
assert_eq!(1, random_string.len());
|
||||
|
||||
let random_string = RandomString::generate(AlphanumericNewline, 100);
|
||||
assert_eq!(100, random_string.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_string_generate_with_delimiter_when_length_is_zero() {
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 0, false, 0);
|
||||
assert_eq!(0, random_string.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_string_generate_with_delimiter_when_num_delimiter_is_greater_than_length() {
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 2, false, 1);
|
||||
assert_eq!(1, random_string.len());
|
||||
assert!(random_string.as_bytes().contains(&0));
|
||||
assert!(random_string.as_bytes().ends_with(&[0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_string_generate_with_delimiter_should_end_with_delimiter() {
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, true, 1);
|
||||
assert_eq!(1, random_string.len());
|
||||
assert_eq!(
|
||||
1,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
assert!(random_string.as_bytes().ends_with(&[0]));
|
||||
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, false, 1);
|
||||
assert_eq!(1, random_string.len());
|
||||
assert_eq!(
|
||||
1,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
assert!(random_string.as_bytes().ends_with(&[0]));
|
||||
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, true, 2);
|
||||
assert_eq!(2, random_string.len());
|
||||
assert_eq!(
|
||||
1,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
assert!(random_string.as_bytes().ends_with(&[0]));
|
||||
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 2, true, 2);
|
||||
assert_eq!(2, random_string.len());
|
||||
assert_eq!(
|
||||
2,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
assert!(random_string.as_bytes().ends_with(&[0]));
|
||||
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, true, 3);
|
||||
assert_eq!(3, random_string.len());
|
||||
assert_eq!(
|
||||
1,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
assert!(random_string.as_bytes().ends_with(&[0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_string_generate_with_delimiter_should_not_end_with_delimiter() {
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 0, false, 1);
|
||||
assert_eq!(1, random_string.len());
|
||||
assert_eq!(
|
||||
0,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 0, true, 1);
|
||||
assert_eq!(1, random_string.len());
|
||||
assert_eq!(
|
||||
0,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, false, 2);
|
||||
assert_eq!(2, random_string.len());
|
||||
assert_eq!(
|
||||
1,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
assert!(!random_string.as_bytes().ends_with(&[0]));
|
||||
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, false, 3);
|
||||
assert_eq!(3, random_string.len());
|
||||
assert_eq!(
|
||||
1,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
assert!(!random_string.as_bytes().ends_with(&[0]));
|
||||
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 2, false, 3);
|
||||
assert_eq!(3, random_string.len());
|
||||
assert_eq!(
|
||||
2,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
assert!(!random_string.as_bytes().ends_with(&[0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_with_delimiter_with_greater_length() {
|
||||
let random_string =
|
||||
RandomString::generate_with_delimiter(Alphanumeric, 0, 100, false, 1000);
|
||||
assert_eq!(1000, random_string.len());
|
||||
assert_eq!(
|
||||
100,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
assert!(!random_string.as_bytes().ends_with(&[0]));
|
||||
|
||||
let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 100, true, 1000);
|
||||
assert_eq!(1000, random_string.len());
|
||||
assert_eq!(
|
||||
100,
|
||||
random_string.as_bytes().iter().filter(|p| **p == 0).count()
|
||||
);
|
||||
assert!(random_string.as_bytes().ends_with(&[0]));
|
||||
}
|
||||
|
||||
/// Originally used to exclude an error within the `random` module. The two
|
||||
/// affected tests timed out on windows, but only in the ci. These tests are
|
||||
/// also the source for the concrete numbers. The timed out tests are
|
||||
/// `test_tail.rs::test_pipe_when_lines_option_given_input_size_has_multiple_size_of_buffer_size`
|
||||
/// `test_tail.rs::test_pipe_when_bytes_option_given_input_size_has_multiple_size_of_buffer_size`.
|
||||
#[test]
|
||||
fn test_generate_random_strings_when_length_is_around_critical_buffer_sizes() {
|
||||
let length = 8192 * 3;
|
||||
let random_string = RandomString::generate(AlphanumericNewline, length);
|
||||
assert_eq!(length, random_string.len());
|
||||
|
||||
let length = 8192 * 3 + 1;
|
||||
let random_string =
|
||||
RandomString::generate_with_delimiter(Alphanumeric, b'\n', 100, true, length);
|
||||
assert_eq!(length, random_string.len());
|
||||
assert_eq!(
|
||||
100,
|
||||
random_string
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.filter(|p| **p == b'\n')
|
||||
.count()
|
||||
);
|
||||
assert!(!random_string.as_bytes().ends_with(&[0]));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[cfg(feature = "renice")]
|
||||
#[path = "by-util/test_renice.rs"]
|
||||
mod test_renice;
|
||||
Reference in New Issue
Block a user