Skip to content

Commit 08564aa

Browse files
authored
Merge branch 'main' into link-edge-case-tests
2 parents 4e7eb3f + d437fe4 commit 08564aa

7 files changed

Lines changed: 61 additions & 43 deletions

File tree

Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -605,9 +605,6 @@ required-features = ["uudoc"]
605605
lto = true
606606
panic = "abort"
607607
codegen-units = 1
608-
# FIXME: https://github.com/uutils/coreutils/issues/10654
609-
[profile.release.package.uu_expand]
610-
codegen-units = 16
611608

612609
# A release-like profile that is as small as possible.
613610
[profile.release-small]

src/uu/expand/src/expand.rs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -362,39 +362,29 @@ enum CharType {
362362
fn classify_char(buf: &[u8], byte: usize, utf8: bool) -> (CharType, usize, usize) {
363363
use self::CharType::{Backspace, Other, Tab};
364364

365-
if utf8 {
366-
let nbytes = char::from(buf[byte]).len_utf8();
365+
let b = buf[byte];
366+
if b.is_ascii() {
367+
return match b {
368+
b'\t' => (Tab, 0, 1),
369+
b'\x08' => (Backspace, 0, 1),
370+
_ => (Other, 1, 1),
371+
};
372+
}
367373

374+
if utf8 {
375+
let nbytes = char::from(b).len_utf8();
368376
let Some(slice) = buf.get(byte..byte + nbytes) else {
369377
// don't overrun buffer because of invalid UTF-8
370378
return (Other, 1, 1);
371379
};
372380

373381
if let Ok(t) = from_utf8(slice) {
374-
match t.chars().next() {
375-
Some('\t') => (Tab, 0, 1),
376-
Some('\x08') => (Backspace, 0, 1),
377-
Some(c) => (Other, UnicodeWidthChar::width(c).unwrap_or(0), nbytes),
378-
None => {
379-
// no valid char at start of t, so take 1 byte
380-
(Other, 1, 1)
381-
}
382+
if let Some(c) = t.chars().next() {
383+
return (Other, UnicodeWidthChar::width(c).unwrap_or(0), nbytes);
382384
}
383-
} else {
384-
(Other, 1, 1) // implicit assumption: non-UTF-8 char is 1 col wide
385385
}
386-
} else {
387-
(
388-
match buf.get(byte) {
389-
// always take exactly 1 byte in strict ASCII mode
390-
Some(0x09) => Tab,
391-
Some(0x08) => Backspace,
392-
_ => Other,
393-
},
394-
0,
395-
1,
396-
)
397386
}
387+
(Other, 1, 1) // implicit assumption: non-UTF-8 char is 1 col wide
398388
}
399389

400390
/// Write spaces for a tab expansion.

src/uu/mktemp/src/mktemp.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,10 @@ impl Options {
131131
(tmpdir, OsString::from(template))
132132
}
133133
Some(template) => {
134-
let tmpdir = if env::var(TMPDIR_ENV_VAR).is_ok() && matches.get_flag(OPT_T) {
135-
env::var_os(TMPDIR_ENV_VAR).map(Into::into)
134+
let tmpdir = if let Some(tmpdir) = env::var_os(TMPDIR_ENV_VAR)
135+
&& matches.get_flag(OPT_T)
136+
{
137+
Some(PathBuf::from(tmpdir))
136138
} else if tmpdir.is_some() {
137139
tmpdir
138140
} else if matches.get_flag(OPT_T) || matches.contains_id(OPT_TMPDIR) {
@@ -390,7 +392,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
390392
// application logic.
391393
let options = Options::from(&matches);
392394

393-
if env::var("POSIXLY_CORRECT").is_ok() {
395+
if env::var_os("POSIXLY_CORRECT").is_some() {
394396
// If POSIXLY_CORRECT was set, template MUST be the last argument.
395397
if matches.contains_id(ARG_TEMPLATE) {
396398
// Template argument was provided, check if was the last one.

src/uu/test/src/test.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,12 +341,15 @@ fn path(path: &OsStr, condition: &PathCondition) -> bool {
341341
PathCondition::Sticky => false,
342342
PathCondition::UserOwns => unimplemented!(),
343343
PathCondition::Fifo => false,
344-
PathCondition::Readable => false, // TODO
344+
PathCondition::Readable => true,
345345
PathCondition::Socket => false,
346346
PathCondition::NonEmpty => stat.len() > 0,
347347
PathCondition::UserIdFlag => false,
348-
PathCondition::Writable => false, // TODO
349-
PathCondition::Executable => false, // TODO
348+
PathCondition::Writable => !stat.permissions().readonly(),
349+
PathCondition::Executable => std::path::Path::new(path)
350+
.extension()
351+
.and_then(|e| e.to_str())
352+
.is_some_and(|e| matches!(e, "exe" | "bat" | "cmd" | "com")),
350353
}
351354
}
352355

src/uucore/src/lib/features/safe_traversal.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use std::path::{Path, PathBuf};
2424
use nix::dir::Dir;
2525
use nix::fcntl::{OFlag, openat};
2626
use nix::libc;
27-
use nix::sys::stat::{FchmodatFlags, FileStat, Mode, fchmodat, fstatat};
27+
use nix::sys::stat::{FchmodatFlags, FileStat, Mode, fchmodat, fstatat, mkdirat};
2828
use nix::unistd::{Gid, Uid, UnlinkatFlags, fchown, fchownat, unlinkat};
2929
use os_display::Quotable;
3030

@@ -323,12 +323,10 @@ impl DirFd {
323323
pub fn mkdir_at(&self, name: &OsStr, mode: u32) -> io::Result<()> {
324324
let name_cstr =
325325
CString::new(name.as_bytes()).map_err(|_| SafeTraversalError::PathContainsNull)?;
326-
let mode = mode as libc::mode_t;
327-
let fd = self.fd.as_raw_fd();
326+
let mode = Mode::from_bits_truncate(mode as libc::mode_t);
328327

329-
let result = unsafe { libc::mkdirat(fd, name_cstr.as_ptr(), mode) };
330-
if result == -1 {
331-
let err = io::Error::last_os_error();
328+
if let Err(e) = mkdirat(self.fd.as_fd(), name_cstr.as_c_str(), mode) {
329+
let err = io::Error::from_raw_os_error(e as i32);
332330
return Err(SafeTraversalError::OpenFailed {
333331
path: name.into(),
334332
source: err,

tests/by-util/test_hostid.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,20 @@ use regex::Regex;
66
use uutests::new_ucmd;
77

88
#[test]
9-
fn test_normal() {
10-
let re = Regex::new(r"^[0-9a-f]{8}").unwrap();
9+
fn test_output_format() {
10+
// Output must be exactly 8 lowercase hex digits followed by a newline.
11+
let re = Regex::new(r"^[0-9a-f]{8}\n$").unwrap();
1112
new_ucmd!().succeeds().stdout_matches(&re);
1213
}
1314

15+
#[test]
16+
fn test_help() {
17+
new_ucmd!()
18+
.arg("--help")
19+
.succeeds()
20+
.stdout_contains("Print the numeric identifier");
21+
}
22+
1423
#[test]
1524
fn test_invalid_flag() {
1625
new_ucmd!()

tests/by-util/test_test.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,6 @@ fn test_file_exists_and_is_regular() {
454454
}
455455

456456
#[test]
457-
#[cfg(not(windows))] // FIXME: implement on Windows
458457
fn test_file_is_readable() {
459458
new_ucmd!().args(&["-r", "regular_file"]).succeeds();
460459
}
@@ -473,7 +472,6 @@ fn test_file_is_not_readable() {
473472
}
474473

475474
#[test]
476-
#[cfg(not(windows))] // FIXME: implement on Windows
477475
fn test_file_is_writable() {
478476
new_ucmd!().args(&["-w", "regular_file"]).succeeds();
479477
}
@@ -517,7 +515,7 @@ fn test_file_is_not_executable() {
517515
}
518516

519517
#[test]
520-
#[cfg(not(windows))] // FIXME: implement on Windows
518+
#[cfg(not(windows))]
521519
fn test_file_is_executable() {
522520
let scenario = TestScenario::new(util_name!());
523521
let mut chmod = scenario.cmd("chmod");
@@ -527,6 +525,27 @@ fn test_file_is_executable() {
527525
scenario.ucmd().args(&["-x", "regular_file"]).succeeds();
528526
}
529527

528+
#[test]
529+
#[cfg(windows)]
530+
fn test_file_is_not_writable_windows() {
531+
let (at, mut ucmd) = at_and_ucmd!();
532+
at.touch("readonly_file");
533+
let mut perms = std::fs::metadata(at.plus("readonly_file"))
534+
.unwrap()
535+
.permissions();
536+
perms.set_readonly(true);
537+
std::fs::set_permissions(at.plus("readonly_file"), perms).unwrap();
538+
ucmd.args(&["!", "-w", "readonly_file"]).succeeds();
539+
}
540+
541+
#[test]
542+
#[cfg(windows)]
543+
fn test_file_is_executable_windows() {
544+
let (at, mut ucmd) = at_and_ucmd!();
545+
at.touch("program.exe");
546+
ucmd.args(&["-x", "program.exe"]).succeeds();
547+
}
548+
530549
#[test]
531550
fn test_is_not_empty() {
532551
new_ucmd!().args(&["-s", "non_empty_file"]).succeeds();

0 commit comments

Comments
 (0)