Merge branch 'master' into chgrp/no-err

This commit is contained in:
Sylvestre Ledru
2021-08-13 01:35:21 +02:00
committed by GitHub
39 changed files with 1360 additions and 1358 deletions
+39
View File
@@ -90,3 +90,42 @@ jobs:
with:
name: gnu-result
path: gnu-result.json
- name: Download the result
uses: dawidd6/action-download-artifact@v2
with:
workflow: GnuTests.yml
name: gnu-result
repo: uutils/coreutils
branch: master
path: dl
- name: Download the log
uses: dawidd6/action-download-artifact@v2
with:
workflow: GnuTests.yml
name: test-report
repo: uutils/coreutils
branch: master
path: dl
- name: Compare failing tests against master
shell: bash
run: |
OLD_FAILING=$(sed -n "s/^FAIL: \([[:print:]]\+\).*/\1/p" dl/test-suite.log | sort)
NEW_FAILING=$(sed -n "s/^FAIL: \([[:print:]]\+\).*/\1/p" gnu/tests/test-suite.log | sort)
for LINE in $OLD_FAILING
do
if ! grep -Fxq $LINE<<<"$NEW_FAILING"; then
echo "::warning ::Congrats! The gnu test $LINE is now passing!"
fi
done
for LINE in $NEW_FAILING
do
if ! grep -Fxq $LINE<<<"$OLD_FAILING"
then
echo "::error ::GNU test failed: $LINE. $LINE is passing on 'master'. Maybe you have to rebase?"
fi
done
- name: Compare against master results
shell: bash
run: |
mv dl/gnu-result.json master-gnu-result.json
python uutils/util/compare_gnu_result.py
Generated
+6 -5
View File
@@ -713,9 +713,9 @@ checksum = "31a7a908b8f32538a2143e59a6e4e2508988832d5d4d6f7c156b3cbc762643a5"
[[package]]
name = "filetime"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
dependencies = [
"cfg-if 1.0.0",
"libc",
@@ -1631,9 +1631,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "selinux"
version = "0.1.3"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd525eeb189eb26c8471463186bba87644e3d8a9c7ae392adaf9ec45ede574bc"
checksum = "1aa2f705dd871c2eb90888bb2d44b13218b34f5c7318c3971df62f799d0143eb"
dependencies = [
"bitflags",
"libc",
@@ -2023,6 +2023,7 @@ dependencies = [
"unix_socket",
"uucore",
"uucore_procs",
"winapi-util",
]
[[package]]
@@ -2032,7 +2033,7 @@ dependencies = [
"clap",
"fts-sys",
"libc",
"selinux-sys",
"selinux",
"thiserror",
"uucore",
"uucore_procs",
+1 -1
View File
@@ -241,7 +241,7 @@ clap = { version = "2.33", features = ["wrap_help"] }
lazy_static = { version="1.3" }
textwrap = { version="=0.11.0", features=["term_size"] } # !maint: [2020-05-10; rivy] unstable crate using undocumented features; pinned currently, will review
uucore = { version=">=0.0.9", package="uucore", path="src/uucore" }
selinux = { version="0.1.3", optional = true }
selinux = { version="0.2.1", optional = true }
# * uutils
uu_test = { optional=true, version="0.0.7", package="uu_test", path="src/uu/test" }
#
+14 -15
View File
@@ -365,25 +365,24 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
| Done | Semi-Done | To Do |
|-----------|-----------|--------|
| arch | cp | chcon |
| base32 | date | runcon |
| base64 | dd | stty |
| arch | cp | runcon |
| base32 | date | stty |
| base64 | dd | |
| basename | df | |
| basenc | expr | |
| cat | install | |
| chcon | join | |
| cat | ls | |
| chgrp | more | |
| chmod | numfmt | |
| chown | od (`--strings` and 128-bit data types missing) | |
| chroot | pr | |
| cksum | printf | |
| comm | sort | |
| csplit | split | |
| cut | tac | |
| dircolors | tail | |
| dirname | test | |
| du | | |
| chgrp | ls | |
| chmod | more | |
| chown | numfmt | |
| chroot | od (`--strings` and 128-bit data types missing) | |
| cksum | pr | |
| comm | printf | |
| csplit | sort | |
| cut | split | |
| dircolors | tac | |
| dirname | tail | |
| du | test | |
| echo | | |
| env | | |
| expand | | |
+3 -2
View File
@@ -23,9 +23,10 @@ uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_p
[target.'cfg(unix)'.dependencies]
unix_socket = "0.5.0"
nix = "0.20.0"
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
nix = "0.20"
[target.'cfg(windows)'.dependencies]
winapi-util = "0.1.5"
[[bin]]
name = "cat"
+90 -28
View File
@@ -22,11 +22,14 @@ use std::io::{self, Read, Write};
use thiserror::Error;
use uucore::error::UResult;
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
/// Linux splice support
#[cfg(any(target_os = "linux", target_os = "android"))]
mod splice;
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::unix::io::RawFd;
/// Unix domain socket support
#[cfg(unix)]
@@ -59,6 +62,8 @@ enum CatError {
},
#[error("Is a directory")]
IsDirectory,
#[error("input file is output file")]
OutputIsInput,
}
type CatResult<T> = Result<T, CatError>;
@@ -123,6 +128,12 @@ struct OutputState {
/// Whether the output cursor is at the beginning of a new line
at_line_start: bool,
/// Whether we skipped a \r, which still needs to be printed
skipped_carriage_return: bool,
/// Whether we have already printed a blank line
one_blank_kept: bool,
}
/// Represents an open file handle, stream, or other device
@@ -297,7 +308,13 @@ fn cat_handle<R: Read>(
}
}
fn cat_path(path: &str, options: &OutputOptions, state: &mut OutputState) -> CatResult<()> {
fn cat_path(
path: &str,
options: &OutputOptions,
state: &mut OutputState,
#[cfg(unix)] out_info: &nix::sys::stat::FileStat,
#[cfg(windows)] out_info: &winapi_util::file::Information,
) -> CatResult<()> {
if path == "-" {
let stdin = io::stdin();
let mut handle = InputHandle {
@@ -324,6 +341,10 @@ fn cat_path(path: &str, options: &OutputOptions, state: &mut OutputState) -> Cat
}
_ => {
let file = File::open(path)?;
#[cfg(any(windows, unix))]
if same_file(out_info, &file) {
return Err(CatError::OutputIsInput);
}
let mut handle = InputHandle {
#[cfg(any(target_os = "linux", target_os = "android"))]
file_descriptor: file.as_raw_fd(),
@@ -335,18 +356,42 @@ fn cat_path(path: &str, options: &OutputOptions, state: &mut OutputState) -> Cat
}
}
#[cfg(unix)]
fn same_file(a_info: &nix::sys::stat::FileStat, b: &File) -> bool {
let b_info = nix::sys::stat::fstat(b.as_raw_fd()).unwrap();
b_info.st_size != 0 && b_info.st_dev == a_info.st_dev && b_info.st_ino == a_info.st_ino
}
#[cfg(windows)]
fn same_file(a_info: &winapi_util::file::Information, b: &File) -> bool {
let b_info = winapi_util::file::information(b).unwrap();
b_info.file_size() != 0
&& b_info.volume_serial_number() == a_info.volume_serial_number()
&& b_info.file_index() == a_info.file_index()
}
fn cat_files(files: Vec<String>, options: &OutputOptions) -> UResult<()> {
#[cfg(windows)]
let out_info = winapi_util::file::information(&std::io::stdout()).unwrap();
#[cfg(unix)]
let out_info = nix::sys::stat::fstat(std::io::stdout().as_raw_fd()).unwrap();
let mut state = OutputState {
line_number: 1,
at_line_start: true,
skipped_carriage_return: false,
one_blank_kept: false,
};
let mut error_messages: Vec<String> = Vec::new();
for path in &files {
if let Err(err) = cat_path(path, options, &mut state) {
if let Err(err) = cat_path(path, options, &mut state, &out_info) {
error_messages.push(format!("{}: {}", path, err));
}
}
if state.skipped_carriage_return {
print!("\r");
}
if error_messages.is_empty() {
Ok(())
} else {
@@ -424,7 +469,6 @@ fn write_lines<R: Read>(
let mut in_buf = [0; 1024 * 31];
let stdout = io::stdout();
let mut writer = stdout.lock();
let mut one_blank_kept = false;
while let Ok(n) = handle.reader.read(&mut in_buf) {
if n == 0 {
@@ -435,8 +479,13 @@ fn write_lines<R: Read>(
while pos < n {
// skip empty line_number enumerating them if needed
if in_buf[pos] == b'\n' {
if !state.at_line_start || !options.squeeze_blank || !one_blank_kept {
one_blank_kept = true;
// \r followed by \n is printed as ^M when show_ends is enabled, so that \r\n prints as ^M$
if state.skipped_carriage_return && options.show_ends {
writer.write_all(b"^M")?;
state.skipped_carriage_return = false;
}
if !state.at_line_start || !options.squeeze_blank || !state.one_blank_kept {
state.one_blank_kept = true;
if state.at_line_start && options.number == NumberingMode::All {
write!(&mut writer, "{0:6}\t", state.line_number)?;
state.line_number += 1;
@@ -450,7 +499,12 @@ fn write_lines<R: Read>(
pos += 1;
continue;
}
one_blank_kept = false;
if state.skipped_carriage_return {
writer.write_all(b"\r")?;
state.skipped_carriage_return = false;
state.at_line_start = false;
}
state.one_blank_kept = false;
if state.at_line_start && options.number != NumberingMode::None {
write!(&mut writer, "{0:6}\t", state.line_number)?;
state.line_number += 1;
@@ -465,17 +519,22 @@ fn write_lines<R: Read>(
write_to_end(&in_buf[pos..], &mut writer)
};
// end of buffer?
if offset == 0 {
if offset + pos == in_buf.len() {
state.at_line_start = false;
break;
}
// print suitable end of line
writer.write_all(options.end_of_line().as_bytes())?;
if handle.is_interactive {
writer.flush()?;
if in_buf[pos + offset] == b'\r' {
state.skipped_carriage_return = true;
} else {
assert_eq!(in_buf[pos + offset], b'\n');
// print suitable end of line
writer.write_all(options.end_of_line().as_bytes())?;
if handle.is_interactive {
writer.flush()?;
}
state.at_line_start = true;
}
state.at_line_start = true;
pos += offset;
pos += offset + 1;
}
}
@@ -483,17 +542,19 @@ fn write_lines<R: Read>(
}
// write***_to_end methods
// Write all symbols till end of line or end of buffer is reached
// Return the (number of written symbols + 1) or 0 if the end of buffer is reached
// Write all symbols till \n or \r or end of buffer is reached
// We need to stop at \r because it may be written as ^M depending on the byte after and settings;
// however, write_nonprint_to_end doesn't need to stop at \r because it will always write \r as ^M.
// Return the number of written symbols
fn write_to_end<W: Write>(in_buf: &[u8], writer: &mut W) -> usize {
match in_buf.iter().position(|c| *c == b'\n') {
match in_buf.iter().position(|c| *c == b'\n' || *c == b'\r') {
Some(p) => {
writer.write_all(&in_buf[..p]).unwrap();
p + 1
p
}
None => {
writer.write_all(in_buf).unwrap();
0
in_buf.len()
}
}
}
@@ -501,20 +562,25 @@ fn write_to_end<W: Write>(in_buf: &[u8], writer: &mut W) -> usize {
fn write_tab_to_end<W: Write>(mut in_buf: &[u8], writer: &mut W) -> usize {
let mut count = 0;
loop {
match in_buf.iter().position(|c| *c == b'\n' || *c == b'\t') {
match in_buf
.iter()
.position(|c| *c == b'\n' || *c == b'\t' || *c == b'\r')
{
Some(p) => {
writer.write_all(&in_buf[..p]).unwrap();
if in_buf[p] == b'\n' {
return count + p + 1;
} else {
return count + p;
} else if in_buf[p] == b'\t' {
writer.write_all(b"^I").unwrap();
in_buf = &in_buf[p + 1..];
count += p + 1;
} else {
return count + p;
}
}
None => {
writer.write_all(in_buf).unwrap();
return 0;
return in_buf.len();
}
};
}
@@ -539,11 +605,7 @@ fn write_nonprint_to_end<W: Write>(in_buf: &[u8], writer: &mut W, tab: &[u8]) ->
.unwrap();
count += 1;
}
if count != in_buf.len() {
count + 1
} else {
0
}
count
}
#[cfg(test)]
+1 -1
View File
@@ -17,7 +17,7 @@ path = "src/chcon.rs"
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version = ">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version = ">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
selinux-sys = { version = "0.5" }
selinux = { version = "0.2" }
fts-sys = { version = "0.2" }
thiserror = { version = "1.0" }
libc = { version = "0.2" }
+134 -563
View File
File diff suppressed because it is too large Load Diff
+71
View File
@@ -0,0 +1,71 @@
use std::ffi::OsString;
use std::fmt::Write;
use std::io;
pub(crate) type Result<T> = std::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)]
pub(crate) enum Error {
#[error("No context is specified")]
MissingContext,
#[error("No files are specified")]
MissingFiles,
#[error("{0}")]
ArgumentsMismatch(String),
#[error(transparent)]
CommandLine(#[from] clap::Error),
#[error("{operation} failed")]
SELinux {
operation: &'static str,
source: selinux::errors::Error,
},
#[error("{operation} failed")]
Io {
operation: &'static str,
source: io::Error,
},
#[error("{operation} failed on '{}'", .operand1.to_string_lossy())]
Io1 {
operation: &'static str,
operand1: OsString,
source: io::Error,
},
}
impl Error {
pub(crate) fn from_io(operation: &'static str, source: io::Error) -> Self {
Self::Io { operation, source }
}
pub(crate) fn from_io1(
operation: &'static str,
operand1: impl Into<OsString>,
source: io::Error,
) -> Self {
Self::Io1 {
operation,
operand1: operand1.into(),
source,
}
}
pub(crate) fn from_selinux(operation: &'static str, source: selinux::errors::Error) -> Self {
Self::SELinux { operation, source }
}
}
pub(crate) fn report_full_error(mut err: &dyn std::error::Error) -> String {
let mut desc = String::with_capacity(256);
write!(&mut desc, "{}", err).unwrap();
while let Some(source) = err.source() {
err = source;
write!(&mut desc, ". {}", err).unwrap();
}
desc
}
+193
View File
@@ -0,0 +1,193 @@
use std::ffi::{CStr, CString, OsStr};
use std::marker::PhantomData;
use std::os::raw::{c_int, c_long, c_short};
use std::path::Path;
use std::ptr::NonNull;
use std::{io, iter, ptr, slice};
use crate::errors::{Error, Result};
use crate::os_str_to_c_string;
#[derive(Debug)]
pub(crate) struct FTS {
fts: ptr::NonNull<fts_sys::FTS>,
entry: Option<ptr::NonNull<fts_sys::FTSENT>>,
_phantom_data: PhantomData<fts_sys::FTSENT>,
}
impl FTS {
pub(crate) fn new<I>(paths: I, options: c_int) -> Result<Self>
where
I: IntoIterator,
I::Item: AsRef<OsStr>,
{
let files_paths: Vec<CString> = paths
.into_iter()
.map(|s| os_str_to_c_string(s.as_ref()))
.collect::<Result<_>>()?;
if files_paths.is_empty() {
return Err(Error::from_io(
"FTS::new()",
io::ErrorKind::InvalidInput.into(),
));
}
let path_argv: Vec<_> = files_paths
.iter()
.map(CString::as_ref)
.map(CStr::as_ptr)
.chain(iter::once(ptr::null()))
.collect();
// SAFETY: We assume calling fts_open() is safe:
// - `path_argv` is an array holding at least one path, and null-terminated.
// - `compar` is None.
let fts = unsafe { fts_sys::fts_open(path_argv.as_ptr().cast(), options, None) };
let fts = ptr::NonNull::new(fts)
.ok_or_else(|| Error::from_io("fts_open()", io::Error::last_os_error()))?;
Ok(Self {
fts,
entry: None,
_phantom_data: PhantomData,
})
}
pub(crate) fn last_entry_ref(&mut self) -> Option<EntryRef> {
self.entry.map(move |entry| EntryRef::new(self, entry))
}
pub(crate) fn read_next_entry(&mut self) -> Result<bool> {
// SAFETY: We assume calling fts_read() is safe with a non-null `fts`
// pointer assumed to be valid.
let new_entry = unsafe { fts_sys::fts_read(self.fts.as_ptr()) };
self.entry = NonNull::new(new_entry);
if self.entry.is_none() {
let r = io::Error::last_os_error();
if let Some(0) = r.raw_os_error() {
Ok(false)
} else {
Err(Error::from_io("fts_read()", r))
}
} else {
Ok(true)
}
}
pub(crate) fn set(&mut self, instr: c_int) -> Result<()> {
let fts = self.fts.as_ptr();
let entry = self
.entry
.ok_or_else(|| Error::from_io("FTS::set()", io::ErrorKind::UnexpectedEof.into()))?;
// SAFETY: We assume calling fts_set() is safe with non-null `fts`
// and `entry` pointers assumed to be valid.
if unsafe { fts_sys::fts_set(fts, entry.as_ptr(), instr) } == -1 {
Err(Error::from_io("fts_set()", io::Error::last_os_error()))
} else {
Ok(())
}
}
}
impl Drop for FTS {
fn drop(&mut self) {
// SAFETY: We assume calling fts_close() is safe with a non-null `fts`
// pointer assumed to be valid.
unsafe { fts_sys::fts_close(self.fts.as_ptr()) };
}
}
#[derive(Debug)]
pub(crate) struct EntryRef<'fts> {
pub(crate) pointer: ptr::NonNull<fts_sys::FTSENT>,
_fts: PhantomData<&'fts FTS>,
_phantom_data: PhantomData<fts_sys::FTSENT>,
}
impl<'fts> EntryRef<'fts> {
fn new(_fts: &'fts FTS, entry: ptr::NonNull<fts_sys::FTSENT>) -> Self {
Self {
pointer: entry,
_fts: PhantomData,
_phantom_data: PhantomData,
}
}
fn as_ref(&self) -> &fts_sys::FTSENT {
// SAFETY: `self.pointer` is a non-null pointer that is assumed to be valid.
unsafe { self.pointer.as_ref() }
}
fn as_mut(&mut self) -> &mut fts_sys::FTSENT {
// SAFETY: `self.pointer` is a non-null pointer that is assumed to be valid.
unsafe { self.pointer.as_mut() }
}
pub(crate) fn flags(&self) -> c_int {
c_int::from(self.as_ref().fts_info)
}
pub(crate) fn errno(&self) -> c_int {
self.as_ref().fts_errno
}
pub(crate) fn level(&self) -> c_short {
self.as_ref().fts_level
}
pub(crate) fn number(&self) -> c_long {
self.as_ref().fts_number
}
pub(crate) fn set_number(&mut self, new_number: c_long) {
self.as_mut().fts_number = new_number;
}
pub(crate) fn path(&self) -> Option<&Path> {
let entry = self.as_ref();
if entry.fts_pathlen == 0 {
return None;
}
NonNull::new(entry.fts_path)
.map(|path_ptr| {
let path_size = usize::from(entry.fts_pathlen).saturating_add(1);
// SAFETY: `entry.fts_path` is a non-null pointer that is assumed to be valid.
unsafe { slice::from_raw_parts(path_ptr.as_ptr().cast(), path_size) }
})
.and_then(|bytes| CStr::from_bytes_with_nul(bytes).ok())
.map(c_str_to_os_str)
.map(Path::new)
}
pub(crate) fn access_path(&self) -> Option<&Path> {
ptr::NonNull::new(self.as_ref().fts_accpath)
.map(|path_ptr| {
// SAFETY: `entry.fts_accpath` is a non-null pointer that is assumed to be valid.
unsafe { CStr::from_ptr(path_ptr.as_ptr()) }
})
.map(c_str_to_os_str)
.map(Path::new)
}
pub(crate) fn stat(&self) -> Option<&libc::stat> {
ptr::NonNull::new(self.as_ref().fts_statp).map(|stat_ptr| {
// SAFETY: `entry.fts_statp` is a non-null pointer that is assumed to be valid.
unsafe { stat_ptr.as_ref() }
})
}
}
#[cfg(unix)]
fn c_str_to_os_str(s: &CStr) -> &OsStr {
use std::os::unix::ffi::OsStrExt;
OsStr::from_bytes(s.to_bytes())
}
+13 -14
View File
@@ -161,12 +161,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Verbosity::Normal
};
let dest_gid: u32;
if let Some(file) = matches.value_of(options::REFERENCE) {
let dest_gid = if let Some(file) = matches.value_of(options::REFERENCE) {
match fs::metadata(&file) {
Ok(meta) => {
dest_gid = meta.gid();
}
Ok(meta) => Some(meta.gid()),
Err(e) => {
show_error!("failed to get attributes of '{}': {}", file, e);
return 1;
@@ -174,16 +171,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
} else {
let group = matches.value_of(options::ARG_GROUP).unwrap_or_default();
match entries::grp2gid(group) {
Ok(g) => {
dest_gid = g;
}
_ => {
show_error!("invalid group: {}", group);
return 1;
if group.is_empty() {
None
} else {
match entries::grp2gid(group) {
Ok(g) => Some(g),
_ => {
show_error!("invalid group: {}", group);
return 1;
}
}
}
}
};
let executor = Chgrper {
bit_flag,
@@ -278,7 +277,7 @@ pub fn uu_app() -> App<'static, 'static> {
}
struct Chgrper {
dest_gid: gid_t,
dest_gid: Option<gid_t>,
bit_flag: u8,
verbosity: Verbosity,
files: Vec<String>,
+3 -6
View File
@@ -14,7 +14,7 @@ use uucore::fs::resolve_relative_path;
use uucore::libc::{gid_t, uid_t};
use uucore::perms::{wrap_chown, Verbosity};
use uucore::error::{FromIo, UError, UResult, USimpleError};
use uucore::error::{FromIo, UResult, USimpleError};
use clap::{crate_version, App, Arg};
@@ -107,10 +107,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if recursive {
if bit_flag == FTS_PHYSICAL {
if derefer == 1 {
return Err(USimpleError::new(
1,
"-R --dereference requires -H or -L".to_string(),
));
return Err(USimpleError::new(1, "-R --dereference requires -H or -L"));
}
derefer = 0;
}
@@ -324,7 +321,7 @@ impl Chowner {
ret |= self.traverse(f);
}
if ret != 0 {
return Err(UError::from(ret));
return Err(ret.into());
}
Ok(())
}
+22 -1
View File
@@ -47,7 +47,7 @@ impl SplitName {
}),
Some(custom) => {
let spec =
Regex::new(r"(?P<ALL>%(?P<FLAG>[0#-])(?P<WIDTH>\d+)?(?P<TYPE>[diuoxX]))")
Regex::new(r"(?P<ALL>%((?P<FLAG>[0#-])(?P<WIDTH>\d+)?)?(?P<TYPE>[diuoxX]))")
.unwrap();
let mut captures_iter = spec.captures_iter(&custom);
let custom_fn: Box<dyn Fn(usize) -> String> = match captures_iter.next() {
@@ -60,6 +60,21 @@ impl SplitName {
Some(m) => m.as_str().parse::<usize>().unwrap(),
};
match (captures.name("FLAG"), captures.name("TYPE")) {
(None, Some(ref t)) => match t.as_str() {
"d" | "i" | "u" => Box::new(move |n: usize| -> String {
format!("{}{}{}{}", prefix, before, n, after)
}),
"o" => Box::new(move |n: usize| -> String {
format!("{}{}{:o}{}", prefix, before, n, after)
}),
"x" => Box::new(move |n: usize| -> String {
format!("{}{}{:x}{}", prefix, before, n, after)
}),
"X" => Box::new(move |n: usize| -> String {
format!("{}{}{:X}{}", prefix, before, n, after)
}),
_ => return Err(CsplitError::SuffixFormatIncorrect),
},
(Some(ref f), Some(ref t)) => {
match (f.as_str(), t.as_str()) {
/*
@@ -276,6 +291,12 @@ mod tests {
assert_eq!(split_name.get(2), "xx00002");
}
#[test]
fn no_padding_decimal() {
let split_name = SplitName::new(None, Some(String::from("cst-%d-")), None).unwrap();
assert_eq!(split_name.get(2), "xxcst-2-");
}
#[test]
fn zero_padding_decimal1() {
let split_name = SplitName::new(None, Some(String::from("cst-%03d-")), None).unwrap();
+2 -2
View File
@@ -8,7 +8,7 @@
#[macro_use]
extern crate uucore;
use uucore::error::UCustomError;
use uucore::error::UError;
use uucore::error::UResult;
#[cfg(unix)]
use uucore::fsext::statfs_fn;
@@ -274,7 +274,7 @@ impl Display for DfError {
impl Error for DfError {}
impl UCustomError for DfError {
impl UError for DfError {
fn code(&self) -> i32 {
match self {
DfError::InvalidBaseValue(_) => 1,
+1 -1
View File
@@ -79,7 +79,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
print!("{}", separator);
}
} else {
return Err(UUsageError::new(1, "missing operand".to_string()));
return Err(UUsageError::new(1, "missing operand"));
}
Ok(())
+2 -2
View File
@@ -32,7 +32,7 @@ use std::path::PathBuf;
use std::str::FromStr;
use std::time::{Duration, UNIX_EPOCH};
use std::{error::Error, fmt::Display};
use uucore::error::{UCustomError, UResult};
use uucore::error::{UError, UResult};
use uucore::parse_size::{parse_size, ParseSizeError};
use uucore::InvalidEncodingHandling;
#[cfg(windows)]
@@ -438,7 +438,7 @@ Try '{} --help' for more information.",
impl Error for DuError {}
impl UCustomError for DuError {
impl UError for DuError {
fn code(&self) -> i32 {
match self {
Self::InvalidMaxDepthArg(_) => 1,
+17 -8
View File
@@ -10,6 +10,7 @@
extern crate uucore;
use std::error::Error;
use std::fmt::Write as FmtWrite;
use std::io::{self, stdin, stdout, BufRead, Write};
mod factor;
@@ -28,21 +29,29 @@ mod options {
pub static NUMBER: &str = "NUMBER";
}
fn print_factors_str(num_str: &str, w: &mut impl io::Write) -> Result<(), Box<dyn Error>> {
num_str
.parse::<u64>()
.map_err(|e| e.into())
.and_then(|x| writeln!(w, "{}:{}", x, factor(x)).map_err(|e| e.into()))
fn print_factors_str(
num_str: &str,
w: &mut io::BufWriter<impl io::Write>,
factors_buffer: &mut String,
) -> Result<(), Box<dyn Error>> {
num_str.parse::<u64>().map_err(|e| e.into()).and_then(|x| {
factors_buffer.clear();
writeln!(factors_buffer, "{}:{}", x, factor(x))?;
w.write_all(factors_buffer.as_bytes())?;
Ok(())
})
}
pub fn uumain(args: impl uucore::Args) -> i32 {
let matches = uu_app().get_matches_from(args);
let stdout = stdout();
let mut w = io::BufWriter::new(stdout.lock());
// We use a smaller buffer here to pass a gnu test. 4KiB appears to be the default pipe size for bash.
let mut w = io::BufWriter::with_capacity(4 * 1024, stdout.lock());
let mut factors_buffer = String::new();
if let Some(values) = matches.values_of(options::NUMBER) {
for number in values {
if let Err(e) = print_factors_str(number, &mut w) {
if let Err(e) = print_factors_str(number, &mut w, &mut factors_buffer) {
show_warning!("{}: {}", number, e);
}
}
@@ -51,7 +60,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
for line in stdin.lock().lines() {
for number in line.unwrap().split_whitespace() {
if let Err(e) = print_factors_str(number, &mut w) {
if let Err(e) = print_factors_str(number, &mut w, &mut factors_buffer) {
show_warning!("{}: {}", number, e);
}
}
+2 -3
View File
@@ -9,13 +9,12 @@
extern crate uucore;
use clap::App;
use uucore::error::{UError, UResult};
use uucore::executable;
use uucore::{error::UResult, executable};
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
uu_app().get_matches_from(args);
Err(UError::from(1))
Err(1.into())
}
pub fn uu_app() -> App<'static, 'static> {
+1 -1
View File
@@ -18,7 +18,7 @@ path = "src/id.rs"
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "process"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
selinux = { version="0.1.3", optional = true }
selinux = { version="0.2.1", optional = true }
[[bin]]
name = "id"
+35 -24
View File
@@ -40,10 +40,10 @@
extern crate uucore;
use clap::{crate_version, App, Arg};
#[cfg(all(target_os = "linux", feature = "selinux"))]
use selinux;
use std::ffi::CStr;
use uucore::entries::{self, Group, Locate, Passwd};
use uucore::error::UResult;
use uucore::error::{set_exit_code, USimpleError};
pub use uucore::libc;
use uucore::libc::{getlogin, uid_t};
use uucore::process::{getegid, geteuid, getgid, getuid};
@@ -123,10 +123,10 @@ struct State {
// 1000 10 968 975
// +++ exited with 0 +++
user_specified: bool,
exit_code: i32,
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = get_usage();
let after_help = get_description();
@@ -161,7 +161,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
},
user_specified: !users.is_empty(),
ids: None,
exit_code: 0,
};
let default_format = {
@@ -170,14 +169,23 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
};
if (state.nflag || state.rflag) && default_format && !state.cflag {
crash!(1, "cannot print only names or real IDs in default format");
return Err(USimpleError::new(
1,
"cannot print only names or real IDs in default format",
));
}
if state.zflag && default_format && !state.cflag {
// NOTE: GNU test suite "id/zero.sh" needs this stderr output:
crash!(1, "option --zero not permitted in default format");
return Err(USimpleError::new(
1,
"option --zero not permitted in default format",
));
}
if state.user_specified && state.cflag {
crash!(1, "cannot print security context when user specified");
return Err(USimpleError::new(
1,
"cannot print security context when user specified",
));
}
let delimiter = {
@@ -204,11 +212,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
print!("{}{}", String::from_utf8_lossy(bytes), line_ending);
} else {
// print error because `cflag` was explicitly requested
crash!(1, "can't get process context");
return Err(USimpleError::new(1, "can't get process context"));
}
return state.exit_code;
return Ok(());
} else {
crash!(1, "--context (-Z) works only on an SELinux-enabled kernel");
return Err(USimpleError::new(
1,
"--context (-Z) works only on an SELinux-enabled kernel",
));
}
}
@@ -220,7 +231,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Ok(p) => Some(p),
Err(_) => {
show_error!("'{}': no such user", users[i]);
state.exit_code = 1;
set_exit_code(1);
if i + 1 >= users.len() {
break;
} else {
@@ -234,17 +245,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if matches.is_present(options::OPT_PASSWORD) {
// BSD's `id` ignores all but the first specified user
pline(possible_pw.map(|v| v.uid()));
return state.exit_code;
return Ok(());
};
if matches.is_present(options::OPT_HUMAN_READABLE) {
// BSD's `id` ignores all but the first specified user
pretty(possible_pw);
return state.exit_code;
return Ok(());
}
if matches.is_present(options::OPT_AUDIT) {
// BSD's `id` ignores specified users
auditid();
return state.exit_code;
return Ok(());
}
let (uid, gid) = possible_pw.map(|p| (p.uid(), p.gid())).unwrap_or((
@@ -264,7 +275,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if state.nflag {
entries::gid2grp(gid).unwrap_or_else(|_| {
show_error!("cannot find name for group ID {}", gid);
state.exit_code = 1;
set_exit_code(1);
gid.to_string()
})
} else {
@@ -279,7 +290,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if state.nflag {
entries::uid2usr(uid).unwrap_or_else(|_| {
show_error!("cannot find name for user ID {}", uid);
state.exit_code = 1;
set_exit_code(1);
uid.to_string()
})
} else {
@@ -304,7 +315,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if state.nflag {
entries::gid2grp(id).unwrap_or_else(|_| {
show_error!("cannot find name for group ID {}", id);
state.exit_code = 1;
set_exit_code(1);
id.to_string()
})
} else {
@@ -332,7 +343,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
}
state.exit_code
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {
@@ -560,7 +571,7 @@ fn id_print(state: &mut State, groups: Vec<u32>) {
uid,
entries::uid2usr(uid).unwrap_or_else(|_| {
show_error!("cannot find name for user ID {}", uid);
state.exit_code = 1;
set_exit_code(1);
uid.to_string()
})
);
@@ -569,7 +580,7 @@ fn id_print(state: &mut State, groups: Vec<u32>) {
gid,
entries::gid2grp(gid).unwrap_or_else(|_| {
show_error!("cannot find name for group ID {}", gid);
state.exit_code = 1;
set_exit_code(1);
gid.to_string()
})
);
@@ -579,7 +590,7 @@ fn id_print(state: &mut State, groups: Vec<u32>) {
euid,
entries::uid2usr(euid).unwrap_or_else(|_| {
show_error!("cannot find name for user ID {}", euid);
state.exit_code = 1;
set_exit_code(1);
euid.to_string()
})
);
@@ -590,7 +601,7 @@ fn id_print(state: &mut State, groups: Vec<u32>) {
euid,
entries::gid2grp(egid).unwrap_or_else(|_| {
show_error!("cannot find name for group ID {}", egid);
state.exit_code = 1;
set_exit_code(1);
egid.to_string()
})
);
@@ -604,7 +615,7 @@ fn id_print(state: &mut State, groups: Vec<u32>) {
gr,
entries::gid2grp(gr).unwrap_or_else(|_| {
show_error!("cannot find name for group ID {}", gr);
state.exit_code = 1;
set_exit_code(1);
gr.to_string()
})
))

Some files were not shown because too many files have changed in this diff Show More