1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Colin Warren <me@zv.ms>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

/* last synced with: unlink (GNU coreutils) 8.21 */

// spell-checker:ignore (ToDO) lstat IFLNK IFMT IFREG

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use libc::{lstat, stat, unlink};
use libc::{S_IFLNK, S_IFMT, S_IFREG};
use std::ffi::CString;
use std::io::{Error, ErrorKind};
use uucore::InvalidEncodingHandling;

static ABOUT: &str = "Unlink the file at [FILE].";
static OPT_PATH: &str = "FILE";

fn get_usage() -> String {
    format!("{} [OPTION]... FILE", executable!())
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let usage = get_usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let paths: Vec<String> = matches
        .values_of(OPT_PATH)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if paths.is_empty() {
        crash!(
            1,
            "missing operand\nTry '{0} --help' for more information.",
            executable!()
        );
    } else if paths.len() > 1 {
        crash!(
            1,
            "extra operand: '{1}'\nTry '{0} --help' for more information.",
            executable!(),
            paths[1]
        );
    }

    let c_string = CString::new(paths[0].clone()).unwrap(); // unwrap() cannot fail, the string comes from argv so it cannot contain a \0.

    let st_mode = {
        #[allow(deprecated)]
        let mut buf: stat = unsafe { std::mem::uninitialized() };
        let result = unsafe { lstat(c_string.as_ptr(), &mut buf as *mut stat) };

        if result < 0 {
            crash!(1, "Cannot stat '{}': {}", paths[0], Error::last_os_error());
        }

        buf.st_mode & S_IFMT
    };

    let result = if st_mode != S_IFREG && st_mode != S_IFLNK {
        Err(Error::new(
            ErrorKind::Other,
            "Not a regular file or symlink",
        ))
    } else {
        let result = unsafe { unlink(c_string.as_ptr()) };

        if result < 0 {
            Err(Error::last_os_error())
        } else {
            Ok(())
        }
    };

    match result {
        Ok(_) => (),
        Err(e) => {
            crash!(1, "cannot unlink '{0}': {1}", paths[0], e);
        }
    }

    0
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .arg(Arg::with_name(OPT_PATH).hidden(true).multiple(true))
}