Add `ln` applet (untested); Switch to _at versions of certain syscalls

due to not existing for every platform
This commit is contained in:
Nathan Fisher 2023-02-05 00:51:11 -05:00
parent 859071698a
commit 59b80e3b85
6 changed files with 169 additions and 15 deletions

View File

@ -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")

View File

@ -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(())
}

View File

@ -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",

View File

@ -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>> {

View File

@ -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 {

View File

@ -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()
)
};