Skip to content

Commit f89458b

Browse files
authored
Merge branch 'main' into patch-3
2 parents 071326c + 39a8c87 commit f89458b

File tree

20 files changed

+376
-254
lines changed

20 files changed

+376
-254
lines changed

.github/workflows/freebsd.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
with:
4444
disable_annotations: true
4545
- name: Prepare, build and test
46-
uses: vmactions/[email protected].8
46+
uses: vmactions/[email protected].9
4747
with:
4848
usesh: true
4949
sync: rsync
@@ -139,7 +139,7 @@ jobs:
139139
with:
140140
disable_annotations: true
141141
- name: Prepare, build and test
142-
uses: vmactions/[email protected].8
142+
uses: vmactions/[email protected].9
143143
with:
144144
usesh: true
145145
sync: rsync

src/common/validation.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub fn get_all_utilities<T: Args>(
2525

2626
/// Prints a "utility not found" error and exits
2727
pub fn not_found(util: &OsStr) -> ! {
28-
println!("{}: function/utility not found", util.maybe_quote());
28+
eprintln!("{}: function/utility not found", util.maybe_quote());
2929
process::exit(1);
3030
}
3131

@@ -51,9 +51,9 @@ fn get_canonical_util_name(util_name: &str) -> &str {
5151
"[" => "test",
5252

5353
// hashsum aliases - all these hash commands are aliases for hashsum
54-
"md5sum" | "sha1sum" | "sha224sum" | "sha256sum" | "sha384sum" | "sha512sum"
55-
| "sha3sum" | "sha3-224sum" | "sha3-256sum" | "sha3-384sum" | "sha3-512sum"
56-
| "shake128sum" | "shake256sum" | "b2sum" | "b3sum" => "hashsum",
54+
"md5sum" | "sha1sum" | "sha224sum" | "sha256sum" | "sha384sum" | "sha512sum" | "b2sum" => {
55+
"hashsum"
56+
}
5757

5858
"dir" => "ls", // dir is an alias for ls
5959

src/uu/install/src/install.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
338338

339339
let specified_mode: Option<u32> = if matches.contains_id(OPT_MODE) {
340340
let x = matches.get_one::<String>(OPT_MODE).ok_or(1)?;
341-
Some(mode::parse(x, considering_dir, 0).map_err(|err| {
341+
Some(uucore::mode::parse(x, considering_dir, 0).map_err(|err| {
342342
show_error!(
343343
"{}",
344344
translate!("install-error-invalid-mode", "error" => err)

src/uu/install/src/mode.rs

Lines changed: 0 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,8 @@
44
// file that was distributed with this source code.
55
use std::fs;
66
use std::path::Path;
7-
#[cfg(not(windows))]
8-
use uucore::mode;
97
use uucore::translate;
108

11-
/// Takes a user-supplied string and tries to parse to u16 mode bitmask.
12-
/// Supports comma-separated mode strings like "ug+rwX,o+rX" (same as chmod).
13-
pub fn parse(mode_string: &str, considering_dir: bool, umask: u32) -> Result<u32, String> {
14-
// Split by commas and process each mode part sequentially
15-
let mut current_mode: u32 = 0;
16-
17-
for mode_part in mode_string.split(',') {
18-
let mode_part = mode_part.trim();
19-
if mode_part.is_empty() {
20-
continue;
21-
}
22-
23-
current_mode = if mode_part.chars().any(|c| c.is_ascii_digit()) {
24-
mode::parse_numeric(current_mode, mode_part, considering_dir)?
25-
} else {
26-
mode::parse_symbolic(current_mode, mode_part, umask, considering_dir)?
27-
};
28-
}
29-
30-
Ok(current_mode)
31-
}
32-
339
/// chmod a file or directory on UNIX.
3410
///
3511
/// Adapted from mkdir.rs. Handles own error printing.
@@ -55,128 +31,3 @@ pub fn chmod(path: &Path, mode: u32) -> Result<(), ()> {
5531
// chmod on Windows only sets the readonly flag, which isn't even honored on directories
5632
Ok(())
5733
}
58-
59-
#[cfg(test)]
60-
#[cfg(not(windows))]
61-
mod tests {
62-
use super::parse;
63-
64-
#[test]
65-
fn test_parse_numeric_mode() {
66-
// Simple numeric mode
67-
assert_eq!(parse("644", false, 0).unwrap(), 0o644);
68-
assert_eq!(parse("755", false, 0).unwrap(), 0o755);
69-
assert_eq!(parse("777", false, 0).unwrap(), 0o777);
70-
assert_eq!(parse("600", false, 0).unwrap(), 0o600);
71-
}
72-
73-
#[test]
74-
fn test_parse_numeric_mode_with_operator() {
75-
// Numeric mode with + operator
76-
assert_eq!(parse("+100", false, 0).unwrap(), 0o100);
77-
assert_eq!(parse("+644", false, 0).unwrap(), 0o644);
78-
79-
// Numeric mode with - operator (starting from 0, so nothing to remove)
80-
assert_eq!(parse("-4", false, 0).unwrap(), 0);
81-
// But if we first set a mode, then remove bits
82-
assert_eq!(parse("644,-4", false, 0).unwrap(), 0o640);
83-
}
84-
85-
#[test]
86-
fn test_parse_symbolic_mode() {
87-
// Simple symbolic modes
88-
assert_eq!(parse("u+x", false, 0).unwrap(), 0o100);
89-
assert_eq!(parse("g+w", false, 0).unwrap(), 0o020);
90-
assert_eq!(parse("o+r", false, 0).unwrap(), 0o004);
91-
assert_eq!(parse("a+x", false, 0).unwrap(), 0o111);
92-
}
93-
94-
#[test]
95-
fn test_parse_symbolic_mode_multiple_permissions() {
96-
// Multiple permissions in one mode
97-
assert_eq!(parse("u+rw", false, 0).unwrap(), 0o600);
98-
assert_eq!(parse("ug+rwx", false, 0).unwrap(), 0o770);
99-
assert_eq!(parse("a+rwx", false, 0).unwrap(), 0o777);
100-
}
101-
102-
#[test]
103-
fn test_parse_comma_separated_modes() {
104-
// Comma-separated mode strings (as mentioned in the doc comment)
105-
assert_eq!(parse("ug+rwX,o+rX", false, 0).unwrap(), 0o664);
106-
assert_eq!(parse("u+rwx,g+rx,o+r", false, 0).unwrap(), 0o754);
107-
assert_eq!(parse("u+w,g+w,o+w", false, 0).unwrap(), 0o222);
108-
}
109-
110-
#[test]
111-
fn test_parse_comma_separated_with_spaces() {
112-
// Comma-separated with spaces (should be trimmed)
113-
assert_eq!(parse("u+rw, g+rw, o+r", false, 0).unwrap(), 0o664);
114-
assert_eq!(parse(" u+x , g+x ", false, 0).unwrap(), 0o110);
115-
}
116-
117-
#[test]
118-
fn test_parse_mixed_numeric_and_symbolic() {
119-
// Mix of numeric and symbolic modes
120-
assert_eq!(parse("644,u+x", false, 0).unwrap(), 0o744);
121-
assert_eq!(parse("u+rw,755", false, 0).unwrap(), 0o755);
122-
}
123-
124-
#[test]
125-
fn test_parse_empty_string() {
126-
// Empty string should return 0
127-
assert_eq!(parse("", false, 0).unwrap(), 0);
128-
assert_eq!(parse(" ", false, 0).unwrap(), 0);
129-
assert_eq!(parse(",,", false, 0).unwrap(), 0);
130-
}
131-
132-
#[test]
133-
fn test_parse_with_umask() {
134-
// Test with umask (affects symbolic modes when no level is specified)
135-
let umask = 0o022;
136-
assert_eq!(parse("+w", false, umask).unwrap(), 0o200);
137-
// The umask should be respected for symbolic modes without explicit level
138-
}
139-
140-
#[test]
141-
fn test_parse_considering_dir() {
142-
// Test directory vs file mode differences
143-
// For directories, X (capital X) should add execute permission
144-
assert_eq!(parse("a+X", true, 0).unwrap(), 0o111);
145-
// For files without execute, X should not add execute
146-
assert_eq!(parse("a+X", false, 0).unwrap(), 0o000);
147-
148-
// Numeric modes for directories preserve setuid/setgid bits
149-
assert_eq!(parse("755", true, 0).unwrap(), 0o755);
150-
}
151-
152-
#[test]
153-
fn test_parse_invalid_modes() {
154-
// Invalid numeric mode (too large)
155-
assert!(parse("10000", false, 0).is_err());
156-
157-
// Invalid operator
158-
assert!(parse("u*rw", false, 0).is_err());
159-
160-
// Invalid symbolic mode
161-
assert!(parse("invalid", false, 0).is_err());
162-
}
163-
164-
#[test]
165-
fn test_parse_complex_combinations() {
166-
// Complex real-world examples
167-
assert_eq!(parse("u=rwx,g=rx,o=r", false, 0).unwrap(), 0o754);
168-
// To test removal, we need to first set permissions, then remove them
169-
assert_eq!(parse("644,a-w", false, 0).unwrap(), 0o444);
170-
assert_eq!(parse("644,g-r", false, 0).unwrap(), 0o604);
171-
}
172-
173-
#[test]
174-
fn test_parse_sequential_application() {
175-
// Test that comma-separated modes are applied sequentially
176-
// First set to 644, then add execute for user
177-
assert_eq!(parse("644,u+x", false, 0).unwrap(), 0o744);
178-
179-
// First add user write, then set to 755 (should override)
180-
assert_eq!(parse("u+w,755", false, 0).unwrap(), 0o755);
181-
}
182-
}

src/uu/mkdir/src/mkdir.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,11 @@ fn get_mode(_matches: &ArgMatches) -> Result<u32, String> {
5757
#[cfg(not(windows))]
5858
fn get_mode(matches: &ArgMatches) -> Result<u32, String> {
5959
// Not tested on Windows
60-
let mut new_mode = DEFAULT_PERM;
61-
6260
if let Some(m) = matches.get_one::<String>(options::MODE) {
63-
for mode in m.split(',') {
64-
if mode.chars().any(|c| c.is_ascii_digit()) {
65-
new_mode = mode::parse_numeric(new_mode, m, true)?;
66-
} else {
67-
new_mode = mode::parse_symbolic(new_mode, mode, mode::get_umask(), true)?;
68-
}
69-
}
70-
Ok(new_mode)
61+
mode::parse_chmod(DEFAULT_PERM, m, true, mode::get_umask())
7162
} else {
7263
// If no mode argument is specified return the mode derived from umask
73-
Ok(!mode::get_umask() & 0o0777)
64+
Ok(!mode::get_umask() & DEFAULT_PERM)
7465
}
7566
}
7667

src/uu/mkfifo/src/mkfifo.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,11 @@ pub fn uu_app() -> Command {
119119

120120
fn calculate_mode(mode_option: Option<&String>) -> Result<u32, String> {
121121
let umask = uucore::mode::get_umask();
122-
let mut mode = 0o666; // Default mode for FIFOs
122+
let mode = 0o666; // Default mode for FIFOs
123123

124124
if let Some(m) = mode_option {
125-
if m.chars().any(|c| c.is_ascii_digit()) {
126-
mode = uucore::mode::parse_numeric(mode, m, false)?;
127-
} else {
128-
for item in m.split(',') {
129-
mode = uucore::mode::parse_symbolic(mode, item, umask, false)?;
130-
}
131-
}
125+
uucore::mode::parse_chmod(mode, m, false, umask)
132126
} else {
133-
mode &= !umask; // Apply umask if no mode is specified
127+
Ok(mode & !umask) // Apply umask if no mode is specified
134128
}
135-
136-
Ok(mode)
137129
}

src/uu/mknod/src/mknod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,10 @@ pub fn uu_app() -> Command {
225225
)
226226
}
227227

228+
#[allow(clippy::unnecessary_cast)]
228229
fn parse_mode(str_mode: &str) -> Result<mode_t, String> {
229-
uucore::mode::parse_mode(str_mode)
230+
let default_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) as u32;
231+
uucore::mode::parse_chmod(default_mode, str_mode, true, uucore::mode::get_umask())
230232
.map_err(|e| {
231233
translate!(
232234
"mknod-error-invalid-mode",
@@ -237,7 +239,7 @@ fn parse_mode(str_mode: &str) -> Result<mode_t, String> {
237239
if mode > 0o777 {
238240
Err(translate!("mknod-error-mode-permission-bits-only"))
239241
} else {
240-
Ok(mode)
242+
Ok(mode as mode_t)
241243
}
242244
})
243245
}

src/uu/ptx/src/ptx.rs

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,6 @@ struct WordRef {
197197

198198
#[derive(Debug, Error)]
199199
enum PtxError {
200-
#[error("{}", translate!("ptx-error-dumb-format"))]
201-
DumbFormat,
202-
203200
#[error("{}", translate!("ptx-error-not-implemented", "feature" => (*.0)))]
204201
NotImplemented(&'static str),
205202

@@ -216,8 +213,6 @@ fn get_config(matches: &clap::ArgMatches) -> UResult<Config> {
216213
config.gnu_ext = false;
217214
config.format = OutFormat::Roff;
218215
"[^ \t\n]+".clone_into(&mut config.context_regex);
219-
} else {
220-
return Err(PtxError::NotImplemented("GNU extensions").into());
221216
}
222217
if matches.contains_id(options::SENTENCE_REGEXP) {
223218
return Err(PtxError::NotImplemented("-S").into());
@@ -589,6 +584,69 @@ fn format_tex_line(
589584
output
590585
}
591586

587+
fn format_dumb_line(
588+
config: &Config,
589+
word_ref: &WordRef,
590+
line: &str,
591+
chars_line: &[char],
592+
reference: &str,
593+
) -> String {
594+
let (tail, before, keyword, after, head) =
595+
prepare_line_chunks(config, word_ref, line, chars_line, reference);
596+
597+
// Calculate the position for the left part
598+
// The left part consists of tail (if present) + space + before
599+
let left_part = if tail.is_empty() {
600+
before
601+
} else if before.is_empty() {
602+
tail
603+
} else {
604+
format!("{tail} {before}")
605+
};
606+
607+
// Calculate the position for the right part
608+
let right_part = if head.is_empty() {
609+
after
610+
} else if after.is_empty() {
611+
head
612+
} else {
613+
format!("{after} {head}")
614+
};
615+
616+
// Calculate the width for the left half (before the keyword)
617+
let half_width = config.line_width / 2;
618+
619+
// Right-justify the left part within the left half
620+
let padding = if left_part.len() < half_width {
621+
half_width - left_part.len()
622+
} else {
623+
0
624+
};
625+
626+
// Build the output line with padding, left part, gap, keyword, and right part
627+
let mut output = String::new();
628+
output.push_str(&" ".repeat(padding));
629+
output.push_str(&left_part);
630+
631+
// Add gap before keyword
632+
output.push_str(&" ".repeat(config.gap_size));
633+
634+
output.push_str(&keyword);
635+
output.push_str(&right_part);
636+
637+
// Add reference if needed
638+
if config.auto_ref || config.input_ref {
639+
if config.right_ref {
640+
output.push(' ');
641+
output.push_str(reference);
642+
} else {
643+
output = format!("{reference} {output}");
644+
}
645+
}
646+
647+
output
648+
}
649+
592650
fn format_roff_field(s: &str) -> String {
593651
s.replace('\"', "\"\"")
594652
}
@@ -716,9 +774,13 @@ fn write_traditional_output(
716774
&chars_lines[word_ref.local_line_nr],
717775
&reference,
718776
),
719-
OutFormat::Dumb => {
720-
return Err(PtxError::DumbFormat.into());
721-
}
777+
OutFormat::Dumb => format_dumb_line(
778+
config,
779+
word_ref,
780+
&lines[word_ref.local_line_nr],
781+
&chars_lines[word_ref.local_line_nr],
782+
&reference,
783+
),
722784
};
723785
writeln!(writer, "{output_line}")
724786
.map_err_context(|| translate!("ptx-error-write-failed"))?;

0 commit comments

Comments
 (0)