moros/src/usr/list.rs

110 lines
3.8 KiB
Rust

use crate::sys;
use crate::api::clock::DATE_TIME;
use crate::api::console::Style;
use crate::api::time;
use crate::api::fs;
use crate::api::fs::FileInfo;
use crate::api::process::ExitCode;
use crate::api::syscall;
use crate::api::unit::SizeUnit;
use alloc::string::ToString;
use alloc::vec::Vec;
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
let mut path: &str = &sys::process::dir(); // TODO: use '.'
let mut sort = "name";
let mut hide_dot_files = true;
let mut unit = SizeUnit::None;
let n = args.len();
for i in 1..n {
match args[i] {
"-h" | "--help" => return help(),
"-a" | "--all" => hide_dot_files = false,
"-n" | "--name" => sort = "name",
"-s" | "--size" => sort = "size",
"-t" | "--time" => sort = "time",
"-b" | "--binary-size" => unit = SizeUnit::Binary,
"-d" | "--decimal-size" => unit = SizeUnit::Decimal,
_ => path = args[i],
}
}
// The commands `list /usr/alice/` and `list /usr/alice` are equivalent,
// but `list /` should not be modified.
if path.len() > 1 {
path = path.trim_end_matches('/');
}
if let Some(info) = syscall::info(path) {
if info.is_dir() {
if let Ok(entries) = fs::read_dir(path) {
let mut files: Vec<_> = entries.iter().filter(|entry| {
!(entry.name().starts_with('.') && hide_dot_files)
}).collect();
match sort {
"name" => files.sort_by_key(|f| f.name()),
"size" => files.sort_by_key(|f| f.size()),
"time" => files.sort_by_key(|f| f.time()),
_ => {
error!("Invalid sort key '{}'", sort);
return Err(ExitCode::Failure);
}
}
let width = files.iter().fold(0, |max_len, file| {
let len = unit.format(file.size() as usize).len();
core::cmp::max(max_len, len)
});
for file in files {
print_file(file, width, unit.clone());
}
Ok(())
} else {
error!("Could not read directory '{}'", path);
Err(ExitCode::Failure)
}
} else {
print_file(&info, info.size().to_string().len(), unit);
Ok(())
}
} else {
error!("Could not find file or directory '{}'", path);
Err(ExitCode::Failure)
}
}
fn print_file(file: &FileInfo, width: usize, unit: SizeUnit) {
let csi_dir_color = Style::color("LightCyan");
let csi_dev_color = Style::color("Yellow");
let csi_reset = Style::reset();
let size = unit.format(file.size() as usize);
let time = time::from_timestamp(file.time() as i64).format(DATE_TIME);
let color = if file.is_dir() {
csi_dir_color
} else if file.is_device() {
csi_dev_color
} else {
csi_reset
};
println!("{:>width$} {} {}{}{}", size, time, color, file.name(), csi_reset, width = width);
}
fn help() -> Result<(), ExitCode> {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} list {}<options> [<dir>]{}", csi_title, csi_reset, csi_option, csi_reset);
println!();
println!("{}Options:{}", csi_title, csi_reset);
println!(" {0}-a{1}, {0}--all{1} Show dot files", csi_option, csi_reset);
println!(" {0}-n{1}, {0}--name{1} Sort by name", csi_option, csi_reset);
println!(" {0}-s{1}, {0}--size{1} Sort by size", csi_option, csi_reset);
println!(" {0}-t{1}, {0}--time{1} Sort by time", csi_option, csi_reset);
Ok(())
}