1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 19:17:43 +00:00

Merge pull request #895 from tarka/ls-uname-group

Add username and groupname lookup to `ls`
This commit is contained in:
Heather 2016-06-05 17:08:20 +04:00
commit 2713758b52

View file

@ -8,37 +8,41 @@
* For the full copyright and license information, please view the LICENSE file * For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code. * that was distributed with this source code.
*/ */
extern crate getopts; extern crate getopts;
extern crate pretty_bytes; extern crate pretty_bytes;
use pretty_bytes::converter::convert; use pretty_bytes::converter::convert;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
extern crate libc;
use self::libc::c_char;
use getopts::Options; use getopts::Options;
use std::fs; use std::fs;
use std::fs::{ReadDir, DirEntry, FileType, Metadata}; use std::fs::{ReadDir, DirEntry, FileType, Metadata};
use std::ffi::{OsString}; use std::ffi::{OsString,CStr};
use std::path::Path; use std::path::Path;
use std::io::Write; use std::io::Write;
use std::ptr;
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
enum Mode { enum Mode {
Help, Help,
Version, Version,
List List
} }
static NAME: &'static str = "ls"; static NAME: &'static str = "ls";
static VERSION: &'static str = env!("CARGO_PKG_VERSION"); static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = Options::new(); let mut opts = Options::new();
opts.optflag("", "help", "display this help and exit"); opts.optflag("", "help", "display this help and exit");
opts.optflag("", "version", "output version information and exit"); opts.optflag("", "version", "output version information and exit");
opts.optflag("a", "all", "Do not ignore hidden files (files with names that start with '.')."); opts.optflag("a", "all", "Do not ignore hidden files (files with names that start with '.').");
opts.optflag("A", "almost-all", "In a directory, do not ignore all file names that start with '.', only ignore '.' and '..'."); opts.optflag("A", "almost-all", "In a directory, do not ignore all file names that start with '.', only ignore '.' and '..'.");
opts.optflag("B", "ignore-backups", "Ignore files that end with ~. Equivalent to using `--ignore='*~'` or `--ignore='.*~'."); opts.optflag("B", "ignore-backups", "Ignore files that end with ~. Equivalent to using `--ignore='*~'` or `--ignore='.*~'.");
@ -53,7 +57,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
panic!() panic!()
}, },
}; };
let mode = if matches.opt_present("version") { let mode = if matches.opt_present("version") {
Mode::Version Mode::Version
} else if matches.opt_present("help") { } else if matches.opt_present("help") {
@ -61,20 +65,20 @@ pub fn uumain(args: Vec<String>) -> i32 {
} else { } else {
Mode::List Mode::List
}; };
match mode { match mode {
Mode::Version => version(), Mode::Version => version(),
Mode::Help => help(), Mode::Help => help(),
Mode::List => list(matches) Mode::List => list(matches)
} }
0 0
} }
fn version() { fn version() {
println!("{} {}", NAME, VERSION); println!("{} {}", NAME, VERSION);
} }
fn help() { fn help() {
let msg = format!("{0} {1}\n\n\ let msg = format!("{0} {1}\n\n\
Usage: {0} [OPTION]... DIRECTORY \n \ Usage: {0} [OPTION]... DIRECTORY \n \
@ -93,15 +97,15 @@ fn list(options: getopts::Matches) {
} else { } else {
options.free.iter().cloned().collect() options.free.iter().cloned().collect()
}; };
for loc in locs { for loc in locs {
let p = Path::new(&loc); let p = Path::new(&loc);
if !p.exists() { if !p.exists() {
show_error!("Cannot find path '{}' because it does not exist.", loc); show_error!("Cannot find path '{}' because it does not exist.", loc);
panic!(); panic!();
} }
if p.is_dir() { if p.is_dir() {
match fs::read_dir(p) { match fs::read_dir(p) {
Err(e) => { Err(e) => {
@ -110,8 +114,8 @@ fn list(options: getopts::Matches) {
}, },
Ok(entries) => enter_directory(entries, &options), Ok(entries) => enter_directory(entries, &options),
}; };
} }
if p.is_file() { if p.is_file() {
display_item(Path::new(p), &options) display_item(Path::new(p), &options)
} }
@ -127,31 +131,78 @@ fn enter_directory(contents: ReadDir, options: &getopts::Matches) {
}, },
Ok(en) => en Ok(en) => en
}; };
// Currently have a DirEntry that we can believe in. // Currently have a DirEntry that we can believe in.
display_dir_entry(entry, options); display_dir_entry(entry, options);
} }
} }
fn display_dir_entry(entry: DirEntry, options: &getopts::Matches) { fn display_dir_entry(entry: DirEntry, options: &getopts::Matches) {
let md = match entry.metadata() { let md = match entry.metadata() {
Err(e) => { Err(e) => {
show_error!("Unable to retrieve metadata for {}. \n Error: {}", show_error!("Unable to retrieve metadata for {}. \n Error: {}",
display_file_name(entry.file_name()), e); display_file_name(entry.file_name()), e);
panic!(); panic!();
}, },
Ok(md) => md Ok(md) => md
}; };
println!(" {}{} {} somebody somegroup {: >9} {}", println!(" {}{} {} {} {} {: >9} {}",
display_file_type(entry.file_type()), display_file_type(entry.file_type()),
display_permissions(&md), display_permissions(&md),
display_symlink_count(&md), display_symlink_count(&md),
display_uname(&md),
display_group(&md),
display_file_size(&md, options), display_file_size(&md, options),
display_file_name(entry.file_name()) display_file_name(entry.file_name())
); );
} }
fn cstr2string(cstr: *const c_char) -> String {
unsafe { String::from_utf8_lossy(CStr::from_ptr(cstr).to_bytes()).to_string() }
}
// Currently getpwuid is `linux` target only. If it's broken out into
// a posix-compliant attribute this can be updated...
#[cfg(target_family = "linux")]
use uucore::c_types::{getpwuid, getgrgid};
#[cfg(target_family = "linux")]
fn display_uname(metadata: &Metadata) -> String {
use std::os::unix::fs::MetadataExt;
let pw = unsafe { getpwuid(metadata.uid()) };
if !pw.is_null() {
cstr2string(unsafe { ptr::read(pw).pw_name })
} else {
metadata.uid().to_string()
}
}
#[cfg(target_family = "linux")]
fn display_group(metadata: &Metadata) -> String {
use std::os::unix::fs::MetadataExt;
let ent = unsafe { getgrgid(metadata.gid()) };
if !ent.is_null() {
cstr2string(unsafe { ptr::read(ent).gr_name })
} else {
metadata.gid().to_string()
}
}
#[cfg(not(target_family = "linux"))]
#[allow(unused_variables)]
fn display_uname(metadata: &Metadata) -> String {
"somebody".to_string()
}
#[cfg(not(target_family = "linux"))]
#[allow(unused_variables)]
fn display_group(metadata: &Metadata) -> String {
"somegroup".to_string()
}
fn display_file_size(metadata: &Metadata, options: &getopts::Matches) -> String { fn display_file_size(metadata: &Metadata, options: &getopts::Matches) -> String {
if options.opt_present("human-readable") { if options.opt_present("human-readable") {
convert(metadata.len() as f64) convert(metadata.len() as f64)
@ -166,9 +217,9 @@ fn display_file_type(file_type: Result<FileType, std::io::Error>) -> String {
show_error!("{}", e); show_error!("{}", e);
panic!() panic!()
}, },
Ok(ft) => ft Ok(ft) => ft
}; };
if file_type.is_dir() { if file_type.is_dir() {
"d".to_string() "d".to_string()
} else if file_type.is_symlink() { } else if file_type.is_symlink() {
@ -193,7 +244,6 @@ fn display_symlink_count(metadata: &Metadata) -> String {
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
fn display_symlink_count(metadata: &Metadata) -> String { fn display_symlink_count(metadata: &Metadata) -> String {
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
metadata.nlink().to_string() metadata.nlink().to_string()
} }
@ -204,7 +254,7 @@ fn display_permissions(metadata: &Metadata) -> String {
} }
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
fn display_permissions(_metadata: &Metadata) -> String { fn display_permissions(_metadata: &Metadata) -> String {
//use std::os::unix::fs::PermissionsExt; //use std::os::unix::fs::PermissionsExt;
"xxxxxxxxx".to_string() "xxxxxxxxx".to_string()
@ -213,15 +263,15 @@ fn display_permissions(_metadata: &Metadata) -> String {
fn display_item(item: &Path, options: &getopts::Matches) { fn display_item(item: &Path, options: &getopts::Matches) {
// let fileType = item.file // let fileType = item.file
// let mut fileMeta = String::new(); // let mut fileMeta = String::new();
// fileMeta = fileMeta + if item.is_dir() { // fileMeta = fileMeta + if item.is_dir() {
// "d" // "d"
// } else if item.sy // } else if item.sy
// } else { // } else {
// "-" // "-"
// }; // };
// println!("{}{}", displayString, item.display()); // println!("{}{}", displayString, item.display());
} }