Added colored output for head and base32 applets (file header is green)
This commit is contained in:
parent
dff8561875
commit
36835dd322
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -2,6 +2,17 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.20.0"
|
||||
@ -98,6 +109,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
@ -134,7 +154,7 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.2.6",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
@ -194,6 +214,7 @@ dependencies = [
|
||||
name = "shitbox"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"base64",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
@ -202,6 +223,7 @@ dependencies = [
|
||||
"data-encoding",
|
||||
"hostname",
|
||||
"once_cell",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2.14"
|
||||
base64 = "0.20.0"
|
||||
clap = "4.0.29"
|
||||
clap_complete = "4.0.6"
|
||||
@ -14,6 +15,7 @@ clap_mangen = "0.2.5"
|
||||
data-encoding = "2.3.3"
|
||||
hostname = { version = "0.3", features = ["set"] }
|
||||
once_cell = "1.16.0"
|
||||
termcolor = "1.1.3"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
@ -3,9 +3,10 @@ use clap::{value_parser, Arg, ArgAction, Command};
|
||||
use data_encoding::BASE32;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Read},
|
||||
io::{self, Read, Write},
|
||||
process,
|
||||
};
|
||||
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Base32 {
|
||||
@ -47,6 +48,10 @@ impl Cmd for Base32 {
|
||||
.long("wrap")
|
||||
.value_parser(value_parser!(usize))
|
||||
.default_value("76"),
|
||||
Arg::new("color")
|
||||
.short('c')
|
||||
.long("color")
|
||||
.value_parser(["always", "ansi", "auto", "never"]),
|
||||
Arg::new("VERBOSE")
|
||||
.help("Display a header naming each file")
|
||||
.short('v')
|
||||
@ -69,12 +74,27 @@ impl Cmd for Base32 {
|
||||
None => vec![String::from("-")],
|
||||
};
|
||||
let len = files.len();
|
||||
let color = match matches.get_one::<String>("color").map(|x| x.as_str()) {
|
||||
Some("always") => ColorChoice::Always,
|
||||
Some("ansi") => ColorChoice::AlwaysAnsi,
|
||||
Some("auto") => {
|
||||
if atty::is(atty::Stream::Stdout) {
|
||||
ColorChoice::Auto
|
||||
} else {
|
||||
ColorChoice::Never
|
||||
}
|
||||
}
|
||||
_ => ColorChoice::Never,
|
||||
};
|
||||
for (index, file) in files.into_iter().enumerate() {
|
||||
if { len > 1 || matches.get_flag("VERBOSE") } && !matches.get_flag("QUIET") {
|
||||
let mut stdout = StandardStream::stdout(color);
|
||||
stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
|
||||
match index {
|
||||
0 => println!("===> {file} <==="),
|
||||
_ => println!("\n===> {file} <==="),
|
||||
};
|
||||
0 => writeln!(stdout, "===> {file} <==="),
|
||||
_ => writeln!(stdout, "\n===> {file} <==="),
|
||||
}?;
|
||||
stdout.reset()?;
|
||||
} else if index > 0 {
|
||||
println!();
|
||||
}
|
||||
|
@ -60,10 +60,10 @@ impl Cmd for Factor {
|
||||
}
|
||||
|
||||
fn first_factor(num: u64) -> u64 {
|
||||
if crate::math::is_prime(num) {
|
||||
return num;
|
||||
if num % 2 == 0 {
|
||||
return 2;
|
||||
}
|
||||
for n in 2..=num {
|
||||
for n in (3..=num).step_by(1) {
|
||||
if num % n == 0 {
|
||||
return n;
|
||||
}
|
||||
|
@ -5,9 +5,10 @@ use std::{
|
||||
env,
|
||||
error::Error,
|
||||
fs,
|
||||
io::{self, stdin, Read},
|
||||
io::{self, stdin, Read, Write},
|
||||
process,
|
||||
};
|
||||
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Head {
|
||||
@ -27,45 +28,49 @@ impl Cmd for Head {
|
||||
|
||||
fn cli(&self) -> Command {
|
||||
Command::new(self.name)
|
||||
.author("Nathan Fisher")
|
||||
.about("Display first lines of a file")
|
||||
.long_about(
|
||||
"Print the first 10 lines of each FILE to standard output.\n\
|
||||
With more than one FILE, precede each with a header giving the file name.\n\n\
|
||||
With no FILE, or when FILE is -, read standard input."
|
||||
)
|
||||
.args([
|
||||
Arg::new("FILES")
|
||||
.help("The input file to use")
|
||||
.num_args(1..),
|
||||
Arg::new("BYTES")
|
||||
.help("Count bytes instead of lines")
|
||||
.short('c')
|
||||
.long("bytes")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("QUIET")
|
||||
.help("Disable printing a header. Overrides -c")
|
||||
.short('q')
|
||||
.long("quiet")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("HEADER")
|
||||
.help("Each file is preceded by a header consisting of the string \"==> XXX <==\" where \"XXX\" is the name of the file.")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("LINES")
|
||||
.help("Count n number of lines (or bytes if -c is specified).")
|
||||
.short('n')
|
||||
.long("lines")
|
||||
.allow_negative_numbers(false)
|
||||
.conflicts_with("num")
|
||||
.value_parser(value_parser!(usize)),
|
||||
Arg::new("num")
|
||||
.short('1')
|
||||
.short_aliases(['2', '3', '4', '5', '6', '7', '8', '9'])
|
||||
.hide(true)
|
||||
.action(ArgAction::Append)
|
||||
])
|
||||
.author("Nathan Fisher")
|
||||
.about("Display first lines of a file")
|
||||
.long_about(
|
||||
"Print the first 10 lines of each FILE to standard output.\n\
|
||||
With more than one FILE, precede each with a header giving the file name.\n\n\
|
||||
With no FILE, or when FILE is -, read standard input."
|
||||
)
|
||||
.args([
|
||||
Arg::new("FILES")
|
||||
.help("The input file to use")
|
||||
.num_args(1..),
|
||||
Arg::new("BYTES")
|
||||
.help("Count bytes instead of lines")
|
||||
.short('c')
|
||||
.long("bytes")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("QUIET")
|
||||
.help("Disable printing a header. Overrides -c")
|
||||
.short('q')
|
||||
.long("quiet")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("HEADER")
|
||||
.help("Each file is preceded by a header consisting of the string \"==> XXX <==\" where \"XXX\" is the name of the file.")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("LINES")
|
||||
.help("Count n number of lines (or bytes if -c is specified).")
|
||||
.short('n')
|
||||
.long("lines")
|
||||
.allow_negative_numbers(false)
|
||||
.conflicts_with("num")
|
||||
.value_parser(value_parser!(usize)),
|
||||
Arg::new("color")
|
||||
.short('C')
|
||||
.long("color")
|
||||
.value_parser(["always", "ansi", "auto", "never"]),
|
||||
Arg::new("num")
|
||||
.short('1')
|
||||
.short_aliases(['2', '3', '4', '5', '6', '7', '8', '9'])
|
||||
.hide(true)
|
||||
.action(ArgAction::Append)
|
||||
])
|
||||
}
|
||||
|
||||
fn run(&self, matches: Option<&ArgMatches>) -> Result<(), Box<dyn Error>> {
|
||||
@ -78,16 +83,17 @@ impl Cmd for Head {
|
||||
if args.len() > idx {
|
||||
let arg1 = &args[idx];
|
||||
if arg1.starts_with('-') && arg1.len() > 1 {
|
||||
let lines: usize = arg1[1..].parse()?;
|
||||
let files: Vec<_> = if args.len() > idx + 1 {
|
||||
args[idx + 1..].iter().map(String::as_str).collect()
|
||||
} else {
|
||||
vec!["-"]
|
||||
};
|
||||
for file in &files {
|
||||
head(file, lines, false, false);
|
||||
if let Ok(lines) = arg1[1..].parse::<usize>() {
|
||||
let files: Vec<_> = if args.len() > idx + 1 {
|
||||
args[idx + 1..].iter().map(String::as_str).collect()
|
||||
} else {
|
||||
vec!["-"]
|
||||
};
|
||||
for file in &files {
|
||||
head(file, lines, false, false, ColorChoice::Never)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let Some(matches) = matches else {
|
||||
@ -102,11 +108,23 @@ impl Cmd for Head {
|
||||
};
|
||||
let header =
|
||||
!matches.get_flag("QUIET") && { files.len() > 1 || matches.get_flag("HEADER") };
|
||||
let color = match matches.get_one::<String>("color").map(|x| x.as_str()) {
|
||||
Some("always") => ColorChoice::Always,
|
||||
Some("ansi") => ColorChoice::AlwaysAnsi,
|
||||
Some("auto") => {
|
||||
if atty::is(atty::Stream::Stdout) {
|
||||
ColorChoice::Auto
|
||||
} else {
|
||||
ColorChoice::Never
|
||||
}
|
||||
}
|
||||
_ => ColorChoice::Never,
|
||||
};
|
||||
for (index, file) in files.into_iter().enumerate() {
|
||||
if index == 1 && header {
|
||||
println!();
|
||||
}
|
||||
head(&file, lines, header, matches.get_flag("BYTES"));
|
||||
head(&file, lines, header, matches.get_flag("BYTES"), color)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -116,7 +134,13 @@ impl Cmd for Head {
|
||||
}
|
||||
}
|
||||
|
||||
fn head(file: &str, count: usize, header: bool, bytes: bool) {
|
||||
fn head(
|
||||
file: &str,
|
||||
count: usize,
|
||||
header: bool,
|
||||
bytes: bool,
|
||||
color: ColorChoice,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut contents = String::new();
|
||||
if file == "-" {
|
||||
match stdin().read_to_string(&mut contents) {
|
||||
@ -137,7 +161,10 @@ fn head(file: &str, count: usize, header: bool, bytes: bool) {
|
||||
};
|
||||
}
|
||||
if header {
|
||||
println!("==> {file} <==");
|
||||
let mut stdout = StandardStream::stdout(color);
|
||||
stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
|
||||
writeln!(stdout, "==> {file} <==")?;
|
||||
stdout.reset()?;
|
||||
}
|
||||
if bytes {
|
||||
for (index, char) in contents.chars().into_iter().enumerate() {
|
||||
@ -145,7 +172,7 @@ fn head(file: &str, count: usize, header: bool, bytes: bool) {
|
||||
print!("{char}");
|
||||
} else {
|
||||
println!();
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
println!();
|
||||
@ -154,8 +181,9 @@ fn head(file: &str, count: usize, header: bool, bytes: bool) {
|
||||
if index < count {
|
||||
println!("{line}");
|
||||
} else {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ pub mod head;
|
||||
pub mod hostname;
|
||||
mod ln;
|
||||
mod ls;
|
||||
mod mountpoint;
|
||||
pub mod mountpoint;
|
||||
mod mv;
|
||||
pub mod nologin;
|
||||
mod pwd;
|
||||
@ -38,9 +38,9 @@ mod sync;
|
||||
pub mod r#true;
|
||||
|
||||
pub use {
|
||||
self::base64::{Base64, BASE_64},
|
||||
self::hostname::{Hostname, HOSTNAME},
|
||||
base32::{Base32, BASE_32},
|
||||
self::base64::{Base64, BASE_64},
|
||||
bootstrap::{Bootstrap, BOOTSTRAP},
|
||||
dirname::{Dirname, DIRNAME},
|
||||
echo::{Echo, ECHO},
|
||||
|
@ -1 +1,50 @@
|
||||
use super::Cmd;
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mountpoint {
|
||||
name: &'static str,
|
||||
path: Option<crate::Path>,
|
||||
}
|
||||
|
||||
pub const MOUNTPOINT: Mountpoint = Mountpoint {
|
||||
name: "mountpoint",
|
||||
path: Some(crate::Path::Bin),
|
||||
};
|
||||
|
||||
impl Cmd for Mountpoint {
|
||||
fn name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn cli(&self) -> clap::Command {
|
||||
Command::new(self.name)
|
||||
.about("see if a directory or file is a mountpoint")
|
||||
.author("Nathan Fisher")
|
||||
.args([
|
||||
Arg::new("fs-devno")
|
||||
.help("Show the major/minor numbers of the device that is mounted on the given directory.")
|
||||
.short('d')
|
||||
.long("fs-devno")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("devno")
|
||||
.help("Show the major/minor numbers of the given blockdevice on standard output.")
|
||||
.short('x')
|
||||
.long("devno")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("quiet")
|
||||
.help("Be quiet - don’t print anything.")
|
||||
.short('q')
|
||||
.long("quiet")
|
||||
.action(ArgAction::SetTrue),
|
||||
])
|
||||
}
|
||||
|
||||
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<crate::Path> {
|
||||
self.path
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::{
|
||||
Cmd, BASE_32, BASE_64, BOOTSTRAP, DIRNAME, ECHO, FACTOR, FALSE, HEAD, HOSTNAME, NOLOGIN, SLEEP, TRUE,
|
||||
Cmd, BASE_32, BASE_64, BOOTSTRAP, DIRNAME, ECHO, FACTOR, FALSE, HEAD, HOSTNAME, NOLOGIN, SLEEP,
|
||||
TRUE,
|
||||
};
|
||||
use clap::Command;
|
||||
use std::{
|
||||
|
@ -3,8 +3,8 @@ use std::{env, error::Error, path::PathBuf, string::ToString};
|
||||
|
||||
pub mod cmd;
|
||||
pub use cmd::{
|
||||
Cmd, Commands, BASE_32, BASE_64, BOOTSTRAP, DIRNAME, ECHO, FACTOR, FALSE, HEAD, HOSTNAME, NOLOGIN,
|
||||
SHITBOX, SLEEP, TRUE,
|
||||
Cmd, Commands, BASE_32, BASE_64, BOOTSTRAP, DIRNAME, ECHO, FACTOR, FALSE, HEAD, HOSTNAME,
|
||||
NOLOGIN, SHITBOX, SLEEP, TRUE,
|
||||
};
|
||||
pub mod math;
|
||||
|
||||
@ -41,8 +41,8 @@ pub fn run() -> Result<(), Box<dyn Error>> {
|
||||
cmd::COMMANDS
|
||||
.set(Commands {
|
||||
items: vec![
|
||||
&BASE_32, &BASE_64, &BOOTSTRAP, &DIRNAME, &ECHO, &FALSE, &FACTOR, &HEAD, &HOSTNAME,
|
||||
&NOLOGIN, &TRUE, &SLEEP, &SHITBOX,
|
||||
&BASE_32, &BASE_64, &BOOTSTRAP, &DIRNAME, &ECHO, &FALSE, &FACTOR, &HEAD,
|
||||
&HOSTNAME, &NOLOGIN, &TRUE, &SLEEP, &SHITBOX,
|
||||
],
|
||||
})
|
||||
.expect("Cannot register commands");
|
||||
|
Loading…
Reference in New Issue
Block a user