From 9716862cfe61bb35615ffe6d08c3d044369aff59 Mon Sep 17 00:00:00 2001 From: Jeremiah Peschka Date: Fri, 25 Mar 2016 14:00:27 -0700 Subject: [PATCH 1/6] Adding skeleton of ls --- Cargo.toml | 2 + Makefile | 1 + src/ls/Cargo.toml | 18 ++++ src/ls/ls.rs | 251 ++++++++++++++++++++++++++++++++++++++++++++++ src/ls/main.rs | 5 + 5 files changed, 277 insertions(+) create mode 100644 src/ls/Cargo.toml create mode 100644 src/ls/ls.rs create mode 100644 src/ls/main.rs diff --git a/Cargo.toml b/Cargo.toml index d5e751f4b..5be88ea6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ generic = [ "head", "link", "ln", + "ls", "mkdir", "nl", "nproc", @@ -119,6 +120,7 @@ id = { optional=true, path="src/id" } kill = { optional=true, path="src/kill" } link = { optional=true, path="src/link" } ln = { optional=true, path="src/ln" } +ls = { optional=true, path="src/ls" } logname = { optional=true, path="src/logname" } mkdir = { optional=true, path="src/mkdir" } mktemp = { optional=true, path="src/mktemp" } diff --git a/Makefile b/Makefile index 19bc50449..be959977e 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ PROGS := \ head \ link \ ln \ + ls \ mkdir \ mktemp \ nl \ diff --git a/src/ls/Cargo.toml b/src/ls/Cargo.toml new file mode 100644 index 000000000..41c5ef8f6 --- /dev/null +++ b/src/ls/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ls" +version = "0.0.1" +authors = ["Jeremiah Peschka "] + +[lib] +name = "uu_ls" +path = "ls.rs" + +[dependencies] +getopts = "*" +libc = "*" +uucore = { path="../uucore" } +pretty-bytes = "0.1.0" + +[[bin]] +name = "ls" +path = "main.rs" diff --git a/src/ls/ls.rs b/src/ls/ls.rs new file mode 100644 index 000000000..3d5453ec2 --- /dev/null +++ b/src/ls/ls.rs @@ -0,0 +1,251 @@ +#![crate_name = "uu_ls"] + +/* + * This file is part of the uutils coreutils package. + * + * (c) Jeremiah Peschka + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +extern crate getopts; +extern crate pretty_bytes; +use pretty_bytes::converter::convert; + +#[macro_use] +extern crate uucore; + +use getopts::Options; +use std::fs; +use std::fs::{ReadDir, DirEntry, FileType, Metadata}; +use std::ffi::{OsString}; +use std::path::Path; +use std::io::Write; + +#[derive(Copy, Clone, PartialEq)] +enum Mode { + Help, + Version, + List +} + +static NAME: &'static str = "ls"; +static VERSION: &'static str = env!("CARGO_PKG_VERSION"); + +pub fn uumain(args: Vec) -> i32 { + let mut opts = Options::new(); + + opts.optflag("", "help", "display this help 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", "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("d", "directory", "Only list the names of directories, rather than listing directory contents. This will not follow symbolic links unless one of `--dereference-command-line (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is specified."); + opts.optflag("H", "dereference-command-line", "If a command line argument specifies a symbolic link, show information about the linked file rather than the link itself."); + opts.optflag("h", "human-readable", "Print human readable file sizes (e.g. 1K 234M 56G)."); + + let matches = match opts.parse(&args[1..]) { + Ok(m) => m, + Err(e) => { + show_error!("{}", e); + panic!() + }, + }; + + let mode = if matches.opt_present("version") { + Mode::Version + } else if matches.opt_present("help") { + Mode::Help + } else { + Mode::List + }; + + match mode { + Mode::Version => version(), + Mode::Help => help(), + Mode::List => list(matches) + } + + 0 +} + +fn version() { + println!("{} {}", NAME, VERSION); +} + +fn help() { + let msg = format!("{0} {1}\n\n\ + Usage: {0} [OPTION]... DIRECTORY \n \ + or: {0} [OPTION]... [FILE]... \n \ + \n \ + By default, ls will list the files and contents of any directories on \ + the command line, expect that it will ignore files and directories \ + whose names start with '.'. \n\ + \n", NAME, VERSION); + println!("{}", msg); +} + +fn list(options: getopts::Matches) { + let locs: Vec = if options.free.is_empty() { + show_error!("Missing DIRECTORY or FILES argument. Try --help."); + panic!(); + } else { + options.free.iter().cloned().collect() + }; + + for loc in locs { + let p = Path::new(&loc); + + if !p.exists() { + show_error!("Cannot find path '{}' because it does not exist.", loc); + panic!(); + } + + if p.is_dir() { + match fs::read_dir(p) { + Err(e) => { + show_error!("Cannot read directory '{}'. \n Reason: {}", loc, e); + panic!(); + }, + Ok(entries) => enter_directory(entries, &options), + }; + } + + if p.is_file() { + display_item(Path::new(p), &options) + } + } +} + +fn enter_directory(contents: ReadDir, options: &getopts::Matches) { + for entry in contents { + let entry = match entry { + Err(err) => { + show_error!("{}", err); + panic!(); + }, + Ok(en) => en + }; + + // Currently have a DirEntry that we can believe in. + display_dir_entry(entry, options); + } +} + +fn display_dir_entry(entry: DirEntry, options: &getopts::Matches) { + let md = match entry.metadata() { + Err(e) => { + show_error!("Unable to retrieve metadata for {}. \n Error: {}", + display_file_name(entry.file_name()), e); + panic!(); + }, + Ok(md) => md + }; + + println!(" {}{} {} somebody somegroup {: >9} {}", + display_file_type(entry.file_type()), + display_permissions(&md), + display_symlink_count(&md), + display_file_size(&md, options), + display_file_name(entry.file_name()) + ); +} + +fn display_file_size(metadata: &Metadata, options: &getopts::Matches) -> String { + if options.opt_present("human-readable") { + convert(metadata.len() as f64) + } else { + metadata.len().to_string() + } +} + +fn display_file_type(file_type: Result) -> String { + let file_type = match file_type { + Err(e) => { + show_error!("{}", e); + panic!() + }, + Ok(ft) => ft + }; + + if file_type.is_dir() { + "d".to_string() + } else if file_type.is_symlink() { + "l".to_string() + } else { + "-".to_string() + } +} + +fn display_file_name(name: OsString) -> String { + name.to_string_lossy().into_owned() +} + +#[cfg(target_family = "windows")] +#[allow(unused_variables)] +fn display_symlink_count(metadata: &Metadata) -> String { + // Currently not sure of how to get this on Windows, so I'm punting. + // Git Bash looks like it may do the same thing. + String::from("1") +} + +#[cfg(target_family = "unix")] +fn display_symlink_count(metadata: &Metadata) -> String { + use std::os::unix::fs::MetadataExt; + + md.nlink().to_string() +} + +#[cfg(target_family = "windows")] +#[allow(unused_variables)] +fn display_permissions(metadata: &Metadata) -> String { + String::from("---------") +} + +#[cfg(target_family = "unix")] +fn display_permissions(metadata: &Metadata) -> String { + use std::os::unix::fs::PermissionsExt; + + "lolwtfbbq".to_string(); +} + + +// fn list_directory_contents(directory: &Path, options: &getopts::Matches) { +// let rd = fs::read_dir(directory); + +// match rd { +// Err(err) => println!("ERROR! {}", err), +// Ok(entries) => display_directory_contents(entries, &options), +// } +// } + +// fn display_directory_contents(contents: ReadDir, options: &getopts::Matches) { +// for entry in contents { +// let entry = match entry { +// Err(err) => { +// println!("ERROR! {}", err); +// panic!() +// }, +// Ok(en) => en +// }; +// display_item(entry.path().as_path(), &options); +// } +// } + +fn display_item(item: &Path, options: &getopts::Matches) { + // let fileType = item.file + // let mut fileMeta = String::new(); + + // fileMeta = fileMeta + if item.is_dir() { + // "d" + // } else if item.sy + // } else { + // "-" + // }; + + + + // println!("{}{}", displayString, item.display()); +} \ No newline at end of file diff --git a/src/ls/main.rs b/src/ls/main.rs new file mode 100644 index 000000000..de1b75e8f --- /dev/null +++ b/src/ls/main.rs @@ -0,0 +1,5 @@ +extern crate uu_ls; + +fn main() { + std::process::exit(uu_ls::uumain(std::env::args().collect())); +} From 825a850fa944a4b1ee4abb030fc6a9145ccfb08b Mon Sep 17 00:00:00 2001 From: Jeremiah Peschka Date: Fri, 25 Mar 2016 14:18:23 -0700 Subject: [PATCH 2/6] Wishing I could spell my own name. --- src/ls/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ls/Cargo.toml b/src/ls/Cargo.toml index 41c5ef8f6..1f5574919 100644 --- a/src/ls/Cargo.toml +++ b/src/ls/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ls" version = "0.0.1" -authors = ["Jeremiah Peschka "] +authors = ["Jeremiah Peschka "] [lib] name = "uu_ls" From 0bb0c40c363a6922900b86ec22178c55613a5425 Mon Sep 17 00:00:00 2001 From: Jeremiah Peschka Date: Fri, 25 Mar 2016 14:25:52 -0700 Subject: [PATCH 3/6] Adding tests for ls. Corrected issue with ls on the present directory throwing panic!() --- Cargo.lock | 20 ++++++++++++++++++++ Makefile | 1 + src/ls/ls.rs | 3 +-- tests/ls.rs | 16 ++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 tests/ls.rs diff --git a/Cargo.lock b/Cargo.lock index db6789500..f2fca3c8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,7 @@ dependencies = [ "link 0.0.1", "ln 0.0.1", "logname 0.0.1", + "ls 0.0.1", "memchr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "mkdir 0.0.1", "mkfifo 0.0.1", @@ -448,6 +449,16 @@ dependencies = [ "uucore 0.0.1", ] +[[package]] +name = "ls" +version = "0.0.1" +dependencies = [ + "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "pretty-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "uucore 0.0.1", +] + [[package]] name = "memchr" version = "0.1.7" @@ -571,6 +582,15 @@ dependencies = [ "uucore 0.0.1", ] +[[package]] +name = "pretty-bytes" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "primal" version = "0.2.3" diff --git a/Makefile b/Makefile index be959977e..ca7ef25f6 100644 --- a/Makefile +++ b/Makefile @@ -146,6 +146,7 @@ TEST_PROGS := \ head \ link \ ln \ + ls \ mkdir \ mktemp \ mv \ diff --git a/src/ls/ls.rs b/src/ls/ls.rs index 3d5453ec2..2e71e7b24 100644 --- a/src/ls/ls.rs +++ b/src/ls/ls.rs @@ -89,8 +89,7 @@ fn help() { fn list(options: getopts::Matches) { let locs: Vec = if options.free.is_empty() { - show_error!("Missing DIRECTORY or FILES argument. Try --help."); - panic!(); + vec![String::from(".")] } else { options.free.iter().cloned().collect() }; diff --git a/tests/ls.rs b/tests/ls.rs new file mode 100644 index 000000000..36d98eea1 --- /dev/null +++ b/tests/ls.rs @@ -0,0 +1,16 @@ +#[macro_use] +mod common; + +use common::util::*; + +static UTIL_NAME: &'static str = "ls"; + +#[test] +fn test_ls_ls() { + let (at, mut ucmd) = testing(UTIL_NAME); + + let result = ucmd.run(); + + let exit_success = result.success; + assert_eq!(exit_success, true); +} \ No newline at end of file From 6e562faae1b51a0806aa8d19706d5178706ae83d Mon Sep 17 00:00:00 2001 From: Jeremiah Peschka Date: Fri, 25 Mar 2016 15:30:25 -0700 Subject: [PATCH 4/6] tabs not spaces --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ca7ef25f6..ca97421e3 100644 --- a/Makefile +++ b/Makefile @@ -146,7 +146,7 @@ TEST_PROGS := \ head \ link \ ln \ - ls \ + ls \ mkdir \ mktemp \ mv \ From 539da3ba1a310f442476a60246cfbb5041a20499 Mon Sep 17 00:00:00 2001 From: Jeremiah Peschka Date: Fri, 25 Mar 2016 16:36:34 -0700 Subject: [PATCH 5/6] Changed display_symlink_count to use the right variable name. Removed dead code. --- src/ls/ls.rs | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/ls/ls.rs b/src/ls/ls.rs index 2e71e7b24..1ac4fcc8b 100644 --- a/src/ls/ls.rs +++ b/src/ls/ls.rs @@ -194,7 +194,7 @@ fn display_symlink_count(metadata: &Metadata) -> String { fn display_symlink_count(metadata: &Metadata) -> String { use std::os::unix::fs::MetadataExt; - md.nlink().to_string() + metadata.nlink().to_string() } #[cfg(target_family = "windows")] @@ -207,32 +207,9 @@ fn display_permissions(metadata: &Metadata) -> String { fn display_permissions(metadata: &Metadata) -> String { use std::os::unix::fs::PermissionsExt; - "lolwtfbbq".to_string(); + "xxxxxxxxx".to_string(); } - -// fn list_directory_contents(directory: &Path, options: &getopts::Matches) { -// let rd = fs::read_dir(directory); - -// match rd { -// Err(err) => println!("ERROR! {}", err), -// Ok(entries) => display_directory_contents(entries, &options), -// } -// } - -// fn display_directory_contents(contents: ReadDir, options: &getopts::Matches) { -// for entry in contents { -// let entry = match entry { -// Err(err) => { -// println!("ERROR! {}", err); -// panic!() -// }, -// Ok(en) => en -// }; -// display_item(entry.path().as_path(), &options); -// } -// } - fn display_item(item: &Path, options: &getopts::Matches) { // let fileType = item.file // let mut fileMeta = String::new(); From e7458b6086fd1d847b9b62219cc8f9bb2b804622 Mon Sep 17 00:00:00 2001 From: Jeremiah Peschka Date: Fri, 25 Mar 2016 18:43:43 -0700 Subject: [PATCH 6/6] Fixing trailing semicolon in display_permissions --- src/ls/ls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ls/ls.rs b/src/ls/ls.rs index 1ac4fcc8b..fa3c6d166 100644 --- a/src/ls/ls.rs +++ b/src/ls/ls.rs @@ -207,7 +207,7 @@ fn display_permissions(metadata: &Metadata) -> String { fn display_permissions(metadata: &Metadata) -> String { use std::os::unix::fs::PermissionsExt; - "xxxxxxxxx".to_string(); + "xxxxxxxxx".to_string() } fn display_item(item: &Path, options: &getopts::Matches) {