Add ln
applet (untested); Switch to _at versions of certain syscalls
due to not existing for every platform
This commit is contained in:
parent
859071698a
commit
59b80e3b85
@ -23,7 +23,7 @@ impl Cmd for B2sum {
|
||||
Arg::new("length")
|
||||
.help(
|
||||
"digest length in bits; must not exceed the max for the \
|
||||
blake2 algorithm and must be a multiple of 8"
|
||||
blake2 algorithm and must be a multiple of 8",
|
||||
)
|
||||
.short('l')
|
||||
.long("length")
|
||||
|
@ -1 +1,136 @@
|
||||
use super::Cmd;
|
||||
use crate::unistd;
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use std::{error::Error, fs, io, os, path::PathBuf};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Ln;
|
||||
|
||||
impl Cmd for Ln {
|
||||
fn cli(&self) -> clap::Command {
|
||||
Command::new("ln")
|
||||
.about("make links between files")
|
||||
.author("Nathan Fisher")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.args([
|
||||
Arg::new("force")
|
||||
.help("remove existing destination files")
|
||||
.short('f')
|
||||
.long("force")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("symbolic")
|
||||
.help("make symbolic links instead of hard links")
|
||||
.short('s')
|
||||
.long("symbolic")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("verbose")
|
||||
.help("print name of each linked file")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("to-target")
|
||||
.help(
|
||||
"For each source_file operand that names a file of type \
|
||||
symbolic link, create a (hard) link to the file referenced \
|
||||
by the symbolic link.",
|
||||
)
|
||||
.short('L')
|
||||
.overrides_with("to-link")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("to-link")
|
||||
.help(
|
||||
"For each source_file operand that names a file of type \
|
||||
symbolic link, create a (hard) link to the symbolic link itself.",
|
||||
)
|
||||
.short('P')
|
||||
.conflicts_with("to-target")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("file")
|
||||
.value_name("source")
|
||||
.num_args(1..)
|
||||
.required(true),
|
||||
Arg::new("dest").num_args(1).required(true),
|
||||
])
|
||||
}
|
||||
|
||||
fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let files = matches
|
||||
.get_many::<String>("file")
|
||||
.unwrap()
|
||||
.collect::<Vec<_>>();
|
||||
let dest = matches.get_one::<String>("dest").unwrap();
|
||||
let dest = PathBuf::from(dest);
|
||||
let sym_target = if matches.get_flag("to-target") {
|
||||
SymTarget::ToTarget
|
||||
} else {
|
||||
SymTarget::ToLink
|
||||
};
|
||||
if files.len() > 1 {
|
||||
if !dest.is_dir() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"destination must be a directory",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
for f in files {
|
||||
let source = PathBuf::from(f);
|
||||
do_link(
|
||||
source,
|
||||
dest.clone(),
|
||||
sym_target,
|
||||
matches.get_flag("force"),
|
||||
matches.get_flag("symbolic"),
|
||||
matches.get_flag("verbose"),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<crate::Path> {
|
||||
Some(crate::Path::Bin)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum SymTarget {
|
||||
ToTarget,
|
||||
ToLink,
|
||||
}
|
||||
|
||||
fn do_link(
|
||||
source: PathBuf,
|
||||
mut dest: PathBuf,
|
||||
sym_target: SymTarget,
|
||||
force: bool,
|
||||
symbolic: bool,
|
||||
verbose: bool,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let source = if source.is_symlink() && sym_target == SymTarget::ToTarget {
|
||||
fs::canonicalize(&source)?
|
||||
} else {
|
||||
source
|
||||
};
|
||||
if dest.is_dir() {
|
||||
let name = source.file_name().unwrap();
|
||||
dest.push(name);
|
||||
}
|
||||
if dest.exists() {
|
||||
if force {
|
||||
unistd::unlink(
|
||||
dest.to_str()
|
||||
.ok_or(io::Error::new(io::ErrorKind::Other, "invalid utf8"))?,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
if symbolic {
|
||||
os::unix::fs::symlink(&source, &dest)?;
|
||||
} else {
|
||||
fs::hard_link(&source, &dest)?;
|
||||
}
|
||||
if verbose {
|
||||
println!("{} -> {}", dest.display(), source.display());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
|
||||
"hostid" => Some(Box::new(hostid::Hostid::default())),
|
||||
"hostname" => Some(Box::new(hostname::Hostname::default())),
|
||||
"link" => Some(Box::new(link::Link::default())),
|
||||
"ln" => Some(Box::new(ln::Ln::default())),
|
||||
"logname" => Some(Box::new(logname::Logname::default())),
|
||||
"md5sum" => Some(Box::new(md5sum::Md5sum::default())),
|
||||
"mkfifo" => Some(Box::new(mkfifo::MkFifo::default())),
|
||||
@ -133,7 +134,7 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
|
||||
}
|
||||
}
|
||||
|
||||
pub static COMMANDS: [&str; 50] = [
|
||||
pub static COMMANDS: [&str; 51] = [
|
||||
"b2sum",
|
||||
"base32",
|
||||
"base64",
|
||||
@ -155,6 +156,7 @@ pub static COMMANDS: [&str; 50] = [
|
||||
"hostid",
|
||||
"hostname",
|
||||
"link",
|
||||
"ln",
|
||||
"logname",
|
||||
"md5sum",
|
||||
"mkfifo",
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::Cmd;
|
||||
use crate::args;
|
||||
use clap::{Arg, Command};
|
||||
use clap::Command;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufRead, BufReader, Write},
|
||||
@ -15,11 +15,7 @@ impl Cmd for Rev {
|
||||
Command::new("rev")
|
||||
.about("reverse lines characterwise")
|
||||
.author("Nathan Fisher")
|
||||
.args([
|
||||
args::header(),
|
||||
args::color(),
|
||||
args::file(),
|
||||
])
|
||||
.args([args::header(), args::color(), args::file()])
|
||||
}
|
||||
|
||||
fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
@ -3,7 +3,15 @@ use std::{ffi::CString, io};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn mknod(path: &str, mode: u32, dev: u64) -> io::Result<()> {
|
||||
let ret = unsafe { syscall!(MKNOD, CString::new(path)?.as_ptr(), mode, dev) };
|
||||
let ret = unsafe {
|
||||
syscall!(
|
||||
MKNODAT,
|
||||
libc::AT_FDCWD,
|
||||
CString::new(path)?.as_ptr(),
|
||||
mode,
|
||||
dev
|
||||
)
|
||||
};
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
@ -13,7 +21,15 @@ pub fn mknod(path: &str, mode: u32, dev: u64) -> io::Result<()> {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn mkfifo(path: &str, mode: u32) -> io::Result<()> {
|
||||
let ret = unsafe { syscall!(MKNOD, CString::new(path)?.as_ptr(), mode | libc::S_IFIFO, 0) };
|
||||
let ret = unsafe {
|
||||
syscall!(
|
||||
MKNODAT,
|
||||
libc::AT_FDCWD,
|
||||
CString::new(path)?.as_ptr(),
|
||||
mode | libc::S_IFIFO,
|
||||
0
|
||||
)
|
||||
};
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -45,9 +45,12 @@ pub fn sethostname(name: &str) -> io::Result<()> {
|
||||
pub fn link(source: &str, dest: &str) -> io::Result<()> {
|
||||
let ret = unsafe {
|
||||
syscall!(
|
||||
LINK,
|
||||
LINKAT,
|
||||
libc::AT_FDCWD,
|
||||
CString::new(source)?.as_ptr(),
|
||||
CString::new(dest)?.as_ptr()
|
||||
libc::AT_FDCWD,
|
||||
CString::new(dest)?.as_ptr(),
|
||||
0
|
||||
)
|
||||
};
|
||||
if ret == 0 {
|
||||
@ -59,7 +62,7 @@ pub fn link(source: &str, dest: &str) -> io::Result<()> {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn unlink(name: &str) -> io::Result<()> {
|
||||
let ret = unsafe { syscall!(UNLINK, CString::new(name)?.as_ptr()) };
|
||||
let ret = unsafe { syscall!(UNLINKAT, libc::AT_FDCWD, CString::new(name)?.as_ptr(), 0) };
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
@ -72,7 +75,8 @@ pub fn readlink(name: &str) -> io::Result<String> {
|
||||
let mut buf = Vec::<i8>::with_capacity(1024);
|
||||
let ret = unsafe {
|
||||
syscall!(
|
||||
READLINK,
|
||||
READLINKAT,
|
||||
libc::AT_FDCWD,
|
||||
CString::new(name)?.as_ptr(),
|
||||
buf.as_mut_ptr(),
|
||||
1024
|
||||
@ -129,8 +133,9 @@ pub fn sync() {
|
||||
pub fn symlink(source: &str, dest: &str) -> io::Result<()> {
|
||||
let ret = unsafe {
|
||||
syscall!(
|
||||
SYMLINK,
|
||||
SYMLINKAT,
|
||||
CString::new(source)?.as_ptr(),
|
||||
libc::AT_FDCWD,
|
||||
CString::new(dest)?.as_ptr()
|
||||
)
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user