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")
|
Arg::new("length")
|
||||||
.help(
|
.help(
|
||||||
"digest length in bits; must not exceed the max for the \
|
"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')
|
.short('l')
|
||||||
.long("length")
|
.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())),
|
"hostid" => Some(Box::new(hostid::Hostid::default())),
|
||||||
"hostname" => Some(Box::new(hostname::Hostname::default())),
|
"hostname" => Some(Box::new(hostname::Hostname::default())),
|
||||||
"link" => Some(Box::new(link::Link::default())),
|
"link" => Some(Box::new(link::Link::default())),
|
||||||
|
"ln" => Some(Box::new(ln::Ln::default())),
|
||||||
"logname" => Some(Box::new(logname::Logname::default())),
|
"logname" => Some(Box::new(logname::Logname::default())),
|
||||||
"md5sum" => Some(Box::new(md5sum::Md5sum::default())),
|
"md5sum" => Some(Box::new(md5sum::Md5sum::default())),
|
||||||
"mkfifo" => Some(Box::new(mkfifo::MkFifo::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",
|
"b2sum",
|
||||||
"base32",
|
"base32",
|
||||||
"base64",
|
"base64",
|
||||||
@ -155,6 +156,7 @@ pub static COMMANDS: [&str; 50] = [
|
|||||||
"hostid",
|
"hostid",
|
||||||
"hostname",
|
"hostname",
|
||||||
"link",
|
"link",
|
||||||
|
"ln",
|
||||||
"logname",
|
"logname",
|
||||||
"md5sum",
|
"md5sum",
|
||||||
"mkfifo",
|
"mkfifo",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::Cmd;
|
use super::Cmd;
|
||||||
use crate::args;
|
use crate::args;
|
||||||
use clap::{Arg, Command};
|
use clap::Command;
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{self, BufRead, BufReader, Write},
|
io::{self, BufRead, BufReader, Write},
|
||||||
@ -15,11 +15,7 @@ impl Cmd for Rev {
|
|||||||
Command::new("rev")
|
Command::new("rev")
|
||||||
.about("reverse lines characterwise")
|
.about("reverse lines characterwise")
|
||||||
.author("Nathan Fisher")
|
.author("Nathan Fisher")
|
||||||
.args([
|
.args([args::header(), args::color(), args::file()])
|
||||||
args::header(),
|
|
||||||
args::color(),
|
|
||||||
args::file(),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
|
fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
@ -3,7 +3,15 @@ use std::{ffi::CString, io};
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn mknod(path: &str, mode: u32, dev: u64) -> io::Result<()> {
|
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 {
|
if ret == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -13,7 +21,15 @@ pub fn mknod(path: &str, mode: u32, dev: u64) -> io::Result<()> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn mkfifo(path: &str, mode: u32) -> io::Result<()> {
|
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 {
|
if ret == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -45,9 +45,12 @@ pub fn sethostname(name: &str) -> io::Result<()> {
|
|||||||
pub fn link(source: &str, dest: &str) -> io::Result<()> {
|
pub fn link(source: &str, dest: &str) -> io::Result<()> {
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
syscall!(
|
syscall!(
|
||||||
LINK,
|
LINKAT,
|
||||||
|
libc::AT_FDCWD,
|
||||||
CString::new(source)?.as_ptr(),
|
CString::new(source)?.as_ptr(),
|
||||||
CString::new(dest)?.as_ptr()
|
libc::AT_FDCWD,
|
||||||
|
CString::new(dest)?.as_ptr(),
|
||||||
|
0
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
@ -59,7 +62,7 @@ pub fn link(source: &str, dest: &str) -> io::Result<()> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn unlink(name: &str) -> io::Result<()> {
|
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 {
|
if ret == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -72,7 +75,8 @@ pub fn readlink(name: &str) -> io::Result<String> {
|
|||||||
let mut buf = Vec::<i8>::with_capacity(1024);
|
let mut buf = Vec::<i8>::with_capacity(1024);
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
syscall!(
|
syscall!(
|
||||||
READLINK,
|
READLINKAT,
|
||||||
|
libc::AT_FDCWD,
|
||||||
CString::new(name)?.as_ptr(),
|
CString::new(name)?.as_ptr(),
|
||||||
buf.as_mut_ptr(),
|
buf.as_mut_ptr(),
|
||||||
1024
|
1024
|
||||||
@ -129,8 +133,9 @@ pub fn sync() {
|
|||||||
pub fn symlink(source: &str, dest: &str) -> io::Result<()> {
|
pub fn symlink(source: &str, dest: &str) -> io::Result<()> {
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
syscall!(
|
syscall!(
|
||||||
SYMLINK,
|
SYMLINKAT,
|
||||||
CString::new(source)?.as_ptr(),
|
CString::new(source)?.as_ptr(),
|
||||||
|
libc::AT_FDCWD,
|
||||||
CString::new(dest)?.as_ptr()
|
CString::new(dest)?.as_ptr()
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user