From 7f3d4ebf5bee2e3004704c8a34a40c657333674e Mon Sep 17 00:00:00 2001 From: Joseph Crail Date: Wed, 13 May 2015 16:38:11 -0400 Subject: [PATCH] Update mkdir. In addition to upgrading to the nightly build, I refactored the method that creates the directories by switching from a recursive approach to an iterative one. I also replaced the obsolete fs::mkdir() with a custom method using fs::create_dir() and libc::chmod(). I added several diagnostic messages that match the GNU implementation. --- src/mkdir/mkdir.rs | 130 +++++++++++++++++++++------------------------ test/mkdir.rs | 40 +++++++------- 2 files changed, 80 insertions(+), 90 deletions(-) diff --git a/src/mkdir/mkdir.rs b/src/mkdir/mkdir.rs index 0659bc53a..914a849ba 100644 --- a/src/mkdir/mkdir.rs +++ b/src/mkdir/mkdir.rs @@ -1,5 +1,5 @@ #![crate_name = "mkdir"] -#![feature(collections, core, old_io, old_path, rustc_private)] +#![feature(path_ext, rustc_private)] /* * This file is part of the uutils coreutils package. @@ -13,9 +13,10 @@ extern crate getopts; extern crate libc; -use std::old_io::fs::{self, PathExtensions}; -use std::old_io::FilePermission; -use std::num::from_str_radix; +use std::ffi::CString; +use std::fs::{self, PathExt}; +use std::io::{Error, Write}; +use std::path::{Path, PathBuf}; #[path = "../common/util.rs"] #[macro_use] @@ -28,7 +29,6 @@ static VERSION: &'static str = "1.0.0"; * Handles option parsing */ pub fn uumain(args: Vec) -> i32 { - let opts = [ // Linux-specific options, not implemented // getopts::optflag("Z", "context", "set SELinux secutiry context" + @@ -41,7 +41,7 @@ pub fn uumain(args: Vec) -> i32 { getopts::optflag("V", "version", "display this version") ]; - let matches = match getopts::getopts(args.tail(), &opts) { + let matches = match getopts::getopts(&args[1..], &opts) { Ok(m) => m, Err(f) => { crash!(1, "Invalid options\n{}", f); @@ -56,32 +56,29 @@ pub fn uumain(args: Vec) -> i32 { println!("mkdir v{}", VERSION); return 0; } - let verbose_flag = matches.opt_present("verbose"); - let mk_parents = matches.opt_present("parents"); + let verbose = matches.opt_present("verbose"); + let recursive = matches.opt_present("parents"); - // Translate a ~str in octal form to u32, default to 755 + // Translate a ~str in octal form to u16, default to 755 // Not tested on Windows let mode_match = matches.opts_str(&["mode".to_string()]); - let mode: FilePermission = if mode_match.is_some() { + let mode: u16 = if mode_match.is_some() { let m = mode_match.unwrap(); - let res: Option = from_str_radix(m.as_slice(), 8).ok(); + let res: Option = u16::from_str_radix(&m, 8).ok(); if res.is_some() { unsafe { std::mem::transmute(res.unwrap()) } } else { crash!(1, "no mode given"); } } else { - unsafe { std::mem::transmute(0o755 as u32) } + unsafe { std::mem::transmute(0o755 as u16) } }; let dirs = matches.free; if dirs.is_empty() { crash!(1, "missing operand"); } - match exec(dirs, mk_parents, mode, verbose_flag) { - Ok(()) => 0, - Err(e) => e - } + exec(dirs, recursive, mode, verbose) } fn print_help(opts: &[getopts::OptGroup]) { @@ -94,68 +91,61 @@ fn print_help(opts: &[getopts::OptGroup]) { /** * Create the list of new directories */ -fn exec(dirs: Vec, mk_parents: bool, mode: FilePermission, verbose: bool) -> Result<(), i32> { - let mut result = Ok(()); - - let mut parent_dirs = Vec::new(); - if mk_parents { - for dir in dirs.iter() { - let path = Path::new((*dir).clone()); - // Build list of parent dirs which need to be created - let parent = path.dirname_str(); - match parent { - Some(p) => { - if !Path::new(p).exists() { - parent_dirs.push(p.to_string()) +fn exec(dirs: Vec, recursive: bool, mode: u16, verbose: bool) -> i32 { + let mut status = 0; + let empty = Path::new(""); + for dir in dirs.iter() { + let path = Path::new(dir); + if recursive { + let mut pathbuf = PathBuf::new(); + for component in path.components() { + pathbuf.push(component.as_os_str()); + status |= mkdir(pathbuf.as_path(), mode, verbose); + } + } else { + match path.parent() { + Some(parent) => { + if parent != empty && !parent.exists() { + show_info!("cannot create directory '{}': No such file or directory", path.display()); + status = 1; + } else { + status |= mkdir(path, mode, verbose); } }, - None => () + None => { + status |= mkdir(path, mode, verbose); + } } } } - // Recursively build parent dirs that are needed - if !parent_dirs.is_empty() { - match exec(parent_dirs, mk_parents, mode, verbose) { - Ok(()) => ( /* keep going */ ), - Err(e) => result = Err(e) - } - } - - for dir in dirs.iter() { - let path = Path::new((*dir).clone()); - // Determine if parent directory to the one to - // be created exists - let parent = match path.dirname_str() { - Some(p) => p, - None => "" - }; - let parent_exists = Path::new(parent).exists(); - if parent_exists && !path.exists() { - mkdir(&path, mode); - if verbose {println!("{}", *dir);} - } else if !mk_parents { - let error_msg = - if !parent_exists { - format!("parent directory '{}' does not exist", parent) - } else { - format!("directory '{}' already exists", *dir) - }; - show_error!("{}", error_msg); - result = Err(1) - } - } - - result + status } /** - * Wrapper to catch errors, return false if failed + * Wrapper to catch errors, return 1 if failed */ -fn mkdir(path: &Path, mode: FilePermission) { - match fs::mkdir(path, mode) { - Ok(_) => {}, - Err(e) => { - crash!(1, "test {}", e.to_string()); - } +fn mkdir(path: &Path, mode: u16, verbose: bool) -> i32 { + if path.exists() { + show_info!("cannot create directory '{}': File exists", path.display()); + return 1; } + + if let Err(e) = fs::create_dir(path) { + show_info!("{}: {}", path.display(), e.to_string()); + return 1; + } + + if verbose { + show_info!("created directory '{}'", path.display()); + } + + let directory = CString::new(path.as_os_str().to_str().unwrap()).unwrap_or_else(|e| crash!(1, "{}", e)); + let mode = mode as libc::mode_t; + + if unsafe { libc::chmod(directory.as_ptr(), mode) } != 0 { + show_info!("{}: errno {}", path.display(), Error::last_os_error().raw_os_error().unwrap()); + return 1; + } + + 0 } diff --git a/test/mkdir.rs b/test/mkdir.rs index 1c54deb2f..860725d3c 100644 --- a/test/mkdir.rs +++ b/test/mkdir.rs @@ -1,29 +1,34 @@ -#![allow(unstable)] +#![feature(path_ext)] -use std::old_io::process::Command; -use std::old_io::fs::{rmdir, PathExtensions}; -use std::borrow::ToOwned; +use std::fs::{remove_dir, PathExt}; +use std::path::Path; +use std::process::{Command, Output}; -static EXE: &'static str = "./mkdir"; +static PROGNAME: &'static str = "./mkdir"; static TEST_DIR1: &'static str = "mkdir_test1"; static TEST_DIR2: &'static str = "mkdir_test2"; static TEST_DIR3: &'static str = "mkdir_test3"; static TEST_DIR4: &'static str = "mkdir_test4/mkdir_test4_1"; static TEST_DIR5: &'static str = "mkdir_test5/mkdir_test5_1"; +fn run(args: &[&'static str]) -> Output { + Command::new(PROGNAME) + .args(args) + .output() + .unwrap_or_else(|e| panic!("{}", e)) +} + fn cleanup(dir: &'static str) { - let d = dir.to_owned(); - let p = Path::new(d.to_owned()); + let p = Path::new(dir); if p.exists() { - rmdir(&p).unwrap(); + remove_dir(&p).unwrap(); } } #[test] fn test_mkdir_mkdir() { cleanup(TEST_DIR1); - let prog = Command::new(EXE).arg(TEST_DIR1).status(); - let exit_success = prog.unwrap().success(); + let exit_success = run(&[TEST_DIR1]).status.success(); cleanup(TEST_DIR1); assert_eq!(exit_success, true); } @@ -31,14 +36,12 @@ fn test_mkdir_mkdir() { #[test] fn test_mkdir_dup_dir() { cleanup(TEST_DIR2); - let prog = Command::new(EXE).arg(TEST_DIR2).status(); - let exit_success = prog.unwrap().success(); + let exit_success = run(&[TEST_DIR2]).status.success(); if !exit_success { cleanup(TEST_DIR2); panic!(); } - let prog2 = Command::new(EXE).arg(TEST_DIR2).status(); - let exit_success2 = prog2.unwrap().success(); + let exit_success2 = run(&[TEST_DIR2]).status.success(); cleanup(TEST_DIR2); assert_eq!(exit_success2, false); } @@ -46,8 +49,7 @@ fn test_mkdir_dup_dir() { #[test] fn test_mkdir_mode() { cleanup(TEST_DIR3); - let prog = Command::new(EXE).arg("-m").arg("755").arg(TEST_DIR3).status(); - let exit_success = prog.unwrap().success(); + let exit_success = run(&["-m", "755", TEST_DIR3]).status.success(); cleanup(TEST_DIR3); assert_eq!(exit_success, true); } @@ -55,8 +57,7 @@ fn test_mkdir_mode() { #[test] fn test_mkdir_parent() { cleanup(TEST_DIR4); - let prog = Command::new(EXE).arg("-p").arg(TEST_DIR4).status(); - let exit_success = prog.unwrap().success(); + let exit_success = run(&["-p", TEST_DIR4]).status.success(); cleanup(TEST_DIR4); assert_eq!(exit_success, true); } @@ -64,8 +65,7 @@ fn test_mkdir_parent() { #[test] fn test_mkdir_no_parent() { cleanup(TEST_DIR5); - let prog = Command::new(EXE).arg(TEST_DIR5).status(); - let exit_success = prog.unwrap().success(); + let exit_success = run(&[TEST_DIR5]).status.success(); cleanup(TEST_DIR5); assert_eq!(exit_success, false); }