mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
Merge branch 'master' into chgrp/no-err
This commit is contained in:
@@ -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
@@ -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
@@ -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" }
|
||||
#
|
||||
|
||||
@@ -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 | | |
|
||||
|
||||
@@ -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
@@ -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)]
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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>,
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user