2014-03-31 16:40:21 +00:00
#![ crate_id(name= " rm " , vers= " 1.0.0 " , author= " Arcterus " ) ]
2013-12-18 06:09:32 +00:00
/*
* This file is part of the uutils coreutils package .
*
* ( c ) Arcterus < arcterus @ mail . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
* /
2014-03-31 16:40:21 +00:00
#![ feature(macro_rules) ]
2014-02-07 06:39:07 +00:00
2014-02-16 21:29:31 +00:00
extern crate getopts ;
2014-04-07 22:43:34 +00:00
extern crate libc ;
2013-12-18 06:09:32 +00:00
use std ::os ;
2014-02-07 06:39:07 +00:00
use std ::io ::{ print , stdin , stdio , fs , BufferedReader } ;
2014-02-23 22:17:48 +00:00
#[ path = " ../common/util.rs " ]
2014-02-07 06:39:07 +00:00
mod util ;
2013-12-18 06:09:32 +00:00
2014-06-01 00:41:32 +00:00
#[ deriving(Eq, PartialEq) ]
2013-12-18 06:09:32 +00:00
enum InteractiveMode {
InteractiveNone ,
InteractiveOnce ,
InteractiveAlways
}
2014-02-07 06:39:07 +00:00
static NAME : & 'static str = " rm " ;
2014-05-28 12:01:30 +00:00
#[ allow(dead_code) ]
2014-06-08 07:56:37 +00:00
fn main ( ) { os ::set_exit_status ( uumain ( os ::args ( ) ) ) ; }
2014-05-28 11:43:37 +00:00
2014-06-08 07:56:37 +00:00
pub fn uumain ( args : Vec < String > ) -> int {
2014-05-16 08:32:58 +00:00
let program = args . get ( 0 ) . clone ( ) ;
2013-12-18 06:09:32 +00:00
// TODO: make getopts support -R in addition to -r
2014-05-30 08:35:54 +00:00
let opts = [
2014-02-07 06:39:07 +00:00
getopts ::optflag ( " f " , " force " , " ignore nonexistent files and arguments, never prompt " ) ,
getopts ::optflag ( " i " , " " , " prompt before every removal " ) ,
getopts ::optflag ( " I " , " " , " prompt once before removing more than three files, or when removing recursively. Less intrusive than -i, while still giving some protection against most mistakes " ) ,
getopts ::optflagopt ( " " , " interactive " , " prompt according to WHEN: never, once (-I), or always (-i). Without WHEN, prompts always " , " WHEN " ) ,
getopts ::optflag ( " " , " one-file-system " , " when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED) " ) ,
getopts ::optflag ( " " , " no-preserve-root " , " do not treat '/' specially " ) ,
getopts ::optflag ( " " , " preserve-root " , " do not remove '/' (default) " ) ,
getopts ::optflag ( " r " , " recursive " , " remove directories and their contents recursively " ) ,
getopts ::optflag ( " d " , " dir " , " remove empty directories " ) ,
getopts ::optflag ( " v " , " verbose " , " explain what is being done " ) ,
getopts ::optflag ( " h " , " help " , " display this help and exit " ) ,
getopts ::optflag ( " V " , " version " , " output version information and exit " )
2013-12-18 06:09:32 +00:00
] ;
2014-02-07 06:39:07 +00:00
let matches = match getopts ::getopts ( args . tail ( ) , opts ) {
2013-12-18 06:09:32 +00:00
Ok ( m ) = > m ,
Err ( f ) = > {
2014-02-07 06:39:07 +00:00
crash! ( 1 , " {} " , f . to_err_msg ( ) )
2013-12-18 06:09:32 +00:00
}
} ;
if matches . opt_present ( " help " ) {
2014-01-13 09:05:02 +00:00
println! ( " rm 1.0.0 " ) ;
println! ( " " ) ;
println! ( " Usage: " ) ;
2013-12-18 06:09:32 +00:00
println! ( " {0:s} [OPTION]... [FILE]... " , program ) ;
2014-01-13 09:05:02 +00:00
println! ( " " ) ;
2014-05-17 10:32:14 +00:00
print ( getopts ::usage ( " Remove (unlink) the FILE(s). " , opts ) . as_slice ( ) ) ;
2014-01-13 09:05:02 +00:00
println! ( " " ) ;
println! ( " By default, rm does not remove directories. Use the --recursive (-r) " ) ;
println! ( " option to remove each listed directory, too, along with all of its contents " ) ;
println! ( " " ) ;
println! ( " To remove a file whose name starts with a '-', for example '-foo', " ) ;
println! ( " use one of these commands: " ) ;
println! ( " rm -- -foo " ) ;
println! ( " " ) ;
println! ( " rm ./-foo " ) ;
println! ( " " ) ;
println! ( " Note that if you use rm to remove a file, it might be possible to recover " ) ;
println! ( " some of its contents, given sufficient expertise and/or time. For greater " ) ;
println! ( " assurance that the contents are truly unrecoverable, consider using shred. " ) ;
2013-12-18 06:09:32 +00:00
} else if matches . opt_present ( " version " ) {
2014-01-13 09:05:02 +00:00
println! ( " rm 1.0.0 " ) ;
2013-12-19 00:11:48 +00:00
} else if matches . free . is_empty ( ) {
2014-06-09 03:26:51 +00:00
show_error! ( " missing an argument " ) ;
show_error! ( " for help, try '{0:s} --help' " , program ) ;
2014-06-10 05:30:54 +00:00
return 1 ;
2013-12-18 06:09:32 +00:00
} else {
let force = matches . opt_present ( " force " ) ;
let interactive =
if matches . opt_present ( " i " ) {
InteractiveAlways
} else if matches . opt_present ( " I " ) {
InteractiveOnce
} else if matches . opt_present ( " interactive " ) {
2014-03-13 23:10:02 +00:00
match matches . opt_str ( " interactive " ) . unwrap ( ) . as_slice ( ) {
" none " = > InteractiveNone ,
" once " = > InteractiveOnce ,
" always " = > InteractiveAlways ,
2013-12-18 06:09:32 +00:00
val = > {
2014-02-07 06:39:07 +00:00
crash! ( 1 , " Invalid argument to interactive ({}) " , val )
2013-12-18 06:09:32 +00:00
}
}
} else {
InteractiveNone
} ;
let one_fs = matches . opt_present ( " one-file-system " ) ;
let preserve_root = ! matches . opt_present ( " no-preserve-root " ) ;
let recursive = matches . opt_present ( " recursive " ) ;
let dir = matches . opt_present ( " dir " ) ;
let verbose = matches . opt_present ( " verbose " ) ;
2013-12-19 00:11:48 +00:00
if interactive = = InteractiveOnce & & ( recursive | | matches . free . len ( ) > 3 ) {
let msg =
if recursive {
" Remove all arguments recursively? "
} else {
" Remove all arguments? "
} ;
if ! prompt ( msg ) {
2014-06-08 07:56:37 +00:00
return 0 ;
2013-12-19 00:11:48 +00:00
}
}
2014-06-09 01:49:06 +00:00
match remove ( matches . free , force , interactive , one_fs , preserve_root ,
recursive , dir , verbose ) {
Ok ( ( ) ) = > ( /* pass */ ) ,
Err ( e ) = > return e
}
2013-12-18 06:09:32 +00:00
}
2014-06-08 07:56:37 +00:00
return 0 ;
2013-12-18 06:09:32 +00:00
}
2013-12-19 00:11:48 +00:00
// TODO: implement one-file-system
2014-06-09 01:49:06 +00:00
fn remove ( files : Vec < String > , force : bool , interactive : InteractiveMode , one_fs : bool , preserve_root : bool , recursive : bool , dir : bool , verbose : bool ) -> Result < ( ) , int > {
2014-06-10 05:30:54 +00:00
let mut r = Ok ( ( ) ) ;
2013-12-18 06:09:32 +00:00
for filename in files . iter ( ) {
2014-05-17 10:32:14 +00:00
let filename = filename . as_slice ( ) ;
let file = Path ::new ( filename ) ;
2013-12-18 06:09:32 +00:00
if file . exists ( ) {
if file . is_dir ( ) {
2014-05-17 10:32:14 +00:00
if recursive & & ( filename ! = " / " | | ! preserve_root ) {
2014-02-05 03:39:17 +00:00
let walk_dir = match fs ::walk_dir ( & file ) {
Ok ( m ) = > m ,
Err ( f ) = > {
2014-02-07 06:39:07 +00:00
crash! ( 1 , " {} " , f . to_str ( ) ) ;
2014-02-05 03:39:17 +00:00
}
} ;
2014-06-10 05:30:54 +00:00
r = remove ( walk_dir . map ( | x | x . as_str ( ) . unwrap ( ) . to_string ( ) ) . collect ( ) , force , interactive , one_fs , preserve_root , recursive , dir , verbose ) . and ( r ) ;
r = remove_dir ( & file , filename , interactive , verbose ) . and ( r ) ;
2014-05-17 10:32:14 +00:00
} else if dir & & ( filename ! = " / " | | ! preserve_root ) {
2014-06-10 05:30:54 +00:00
r = remove_dir ( & file , filename , interactive , verbose ) . and ( r ) ;
2013-12-18 06:09:32 +00:00
} else {
2013-12-18 06:17:05 +00:00
if recursive {
2014-06-09 03:26:51 +00:00
show_error! ( " could not remove directory '{}' " ,
2014-05-17 10:32:14 +00:00
filename ) ;
2014-06-10 05:30:54 +00:00
r = Err ( 1 ) ;
2013-12-18 06:17:05 +00:00
} else {
2014-06-09 03:26:51 +00:00
show_error! ( " could not remove directory '{}' (did you mean to pass '-r'?) " ,
2014-05-17 10:32:14 +00:00
filename ) ;
2014-06-10 05:30:54 +00:00
r = Err ( 1 ) ;
2013-12-18 06:17:05 +00:00
}
2013-12-18 06:09:32 +00:00
}
} else {
2014-06-10 05:30:54 +00:00
r = remove_file ( & file , filename . as_slice ( ) , interactive , verbose ) . and ( r ) ;
2013-12-18 06:09:32 +00:00
}
} else if ! force {
2014-06-09 03:26:51 +00:00
show_error! ( " no such file or directory '{}' " , filename ) ;
2014-06-10 05:30:54 +00:00
r = Err ( 1 ) ;
2013-12-18 06:09:32 +00:00
}
}
2014-06-09 01:49:06 +00:00
2014-06-10 05:30:54 +00:00
return r ;
2013-12-18 06:09:32 +00:00
}
2014-06-09 01:49:06 +00:00
fn remove_dir ( path : & Path , name : & str , interactive : InteractiveMode , verbose : bool ) -> Result < ( ) , int > {
2013-12-19 00:11:48 +00:00
let response =
if interactive = = InteractiveAlways {
prompt_file ( path , name )
} else {
true
} ;
if response {
2014-02-05 03:39:17 +00:00
match fs ::rmdir ( path ) {
Ok ( _ ) = > if verbose { println! ( " Removed ' {} ' " , name ) ; } ,
Err ( f ) = > {
2014-06-09 03:26:51 +00:00
show_error! ( " {} " , f . to_str ( ) ) ;
2014-06-09 01:49:06 +00:00
return Err ( 1 ) ;
2013-12-19 00:11:48 +00:00
}
2014-02-05 03:39:17 +00:00
}
2013-12-19 00:11:48 +00:00
}
2014-06-09 01:49:06 +00:00
return Ok ( ( ) ) ;
2013-12-19 00:11:48 +00:00
}
2014-06-09 01:49:06 +00:00
fn remove_file ( path : & Path , name : & str , interactive : InteractiveMode , verbose : bool ) -> Result < ( ) , int > {
2013-12-19 00:11:48 +00:00
let response =
if interactive = = InteractiveAlways {
prompt_file ( path , name )
} else {
true
} ;
if response {
2014-02-05 03:39:17 +00:00
match fs ::unlink ( path ) {
Ok ( _ ) = > if verbose { println! ( " Removed ' {} ' " , name ) ; } ,
Err ( f ) = > {
2014-06-09 03:26:51 +00:00
show_error! ( " {} " , f . to_str ( ) ) ;
2014-06-09 01:49:06 +00:00
return Err ( 1 ) ;
2013-12-19 00:11:48 +00:00
}
2014-02-05 03:39:17 +00:00
}
2013-12-19 00:11:48 +00:00
}
2014-06-09 01:49:06 +00:00
return Ok ( ( ) ) ;
2013-12-19 00:11:48 +00:00
}
fn prompt_file ( path : & Path , name : & str ) -> bool {
if path . is_dir ( ) {
2014-05-23 12:28:40 +00:00
prompt ( format! ( " Remove directory ' {} '? " , name ) . as_slice ( ) )
2013-12-19 00:11:48 +00:00
} else {
2014-05-23 12:28:40 +00:00
prompt ( format! ( " Remove file ' {} '? " , name ) . as_slice ( ) )
2013-12-19 00:11:48 +00:00
}
}
fn prompt ( msg : & str ) -> bool {
print ( msg ) ;
read_prompt ( )
}
fn read_prompt ( ) -> bool {
stdio ::flush ( ) ;
2014-01-21 06:50:38 +00:00
match BufferedReader ::new ( stdin ( ) ) . read_line ( ) {
2014-02-05 03:39:17 +00:00
Ok ( line ) = > {
2014-05-23 12:28:40 +00:00
match line . as_slice ( ) . char_at ( 0 ) {
2013-12-19 00:11:48 +00:00
'y' | 'Y' = > true ,
'n' | 'N' = > false ,
_ = > {
2014-01-13 09:05:02 +00:00
print! ( " Please enter either Y or N: " ) ;
2013-12-19 00:11:48 +00:00
read_prompt ( )
}
}
}
2014-02-05 03:39:17 +00:00
Err ( _ ) = > true
2013-12-19 00:11:48 +00:00
}
}