Skip to content

Commit 7811547

Browse files
authored
Merge branch 'main' into cksum_dir
2 parents ab71fe2 + 5b860cb commit 7811547

22 files changed

+610
-79
lines changed

.github/workflows/android.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
ram-size: 2048M
5353
disk-size: 7GB
5454
force-avd-creation: true
55-
emulator-options: -no-snapshot-load -noaudio -no-boot-anim -camera-back none
55+
emulator-options: -no-window -no-snapshot-load -noaudio -no-boot-anim -camera-back none
5656
script: |
5757
util/android-commands.sh init "${{ matrix.arch }}" "${{ matrix.api-level }}" "${{ env.TERMUX }}"
5858
- name: Save AVD cache
@@ -88,7 +88,7 @@ jobs:
8888
ram-size: 2048M
8989
disk-size: 7GB
9090
force-avd-creation: false
91-
emulator-options: -no-snapshot-save -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -snapshot ${{ matrix.api-level }}-${{ matrix.arch }}+termux-${{ env.TERMUX }}
91+
emulator-options: -no-window -no-snapshot-save -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -snapshot ${{ matrix.api-level }}-${{ matrix.arch }}+termux-${{ env.TERMUX }}
9292
# This is not a usual script. Every line is executed in a separate shell with `sh -c`. If
9393
# one of the lines returns with error the whole script is failed (like running a script with
9494
# set -e) and in consequences the other lines (shells) are not executed.

src/uu/cksum/src/cksum.rs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55

66
// spell-checker:ignore (ToDO) fname, algo
77
use clap::{crate_version, value_parser, Arg, ArgAction, Command};
8+
use hex::decode;
89
use hex::encode;
10+
use std::error::Error;
911
use std::ffi::OsStr;
12+
use std::fmt::Display;
1013
use std::fs::File;
11-
use std::io::{self, stdin, BufReader, Read};
14+
use std::io::{self, stdin, stdout, BufReader, Read, Write};
1215
use std::iter;
1316
use std::path::Path;
1417
use uucore::{
15-
error::{FromIo, UResult},
18+
error::{FromIo, UError, UResult},
1619
format_usage, help_about, help_section, help_usage,
1720
sum::{
1821
div_ceil, Blake2b, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha512, Sm3,
@@ -36,6 +39,31 @@ const ALGORITHM_OPTIONS_SHA512: &str = "sha512";
3639
const ALGORITHM_OPTIONS_BLAKE2B: &str = "blake2b";
3740
const ALGORITHM_OPTIONS_SM3: &str = "sm3";
3841

42+
#[derive(Debug)]
43+
enum CkSumError {
44+
RawMultipleFiles,
45+
}
46+
47+
impl UError for CkSumError {
48+
fn code(&self) -> i32 {
49+
match self {
50+
Self::RawMultipleFiles => 1,
51+
}
52+
}
53+
}
54+
55+
impl Error for CkSumError {}
56+
57+
impl Display for CkSumError {
58+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59+
match self {
60+
Self::RawMultipleFiles => {
61+
write!(f, "the --raw option is not supported with multiple files")
62+
}
63+
}
64+
}
65+
}
66+
3967
fn detect_algo(
4068
program: &str,
4169
length: Option<usize>,
@@ -110,6 +138,7 @@ struct Options {
110138
output_bits: usize,
111139
untagged: bool,
112140
length: Option<usize>,
141+
raw: bool,
113142
}
114143

115144
/// Calculate checksum
@@ -123,6 +152,11 @@ fn cksum<'a, I>(mut options: Options, files: I) -> UResult<()>
123152
where
124153
I: Iterator<Item = &'a OsStr>,
125154
{
155+
let files: Vec<_> = files.collect();
156+
if options.raw && files.len() > 1 {
157+
return Err(Box::new(CkSumError::RawMultipleFiles));
158+
}
159+
126160
for filename in files {
127161
let filename = Path::new(filename);
128162
let stdin_buf;
@@ -146,6 +180,17 @@ where
146180
format!("{}: Is a directory", filename.display()),
147181
)
148182
.into());
183+
184+
if options.raw {
185+
let bytes = match options.algo_name {
186+
ALGORITHM_OPTIONS_CRC => sum.parse::<u32>().unwrap().to_be_bytes().to_vec(),
187+
ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => {
188+
sum.parse::<u16>().unwrap().to_be_bytes().to_vec()
189+
}
190+
_ => decode(sum).unwrap(),
191+
};
192+
stdout().write_all(&bytes)?;
193+
return Ok(());
149194
}
150195
// The BSD checksum output is 5 digit integer
151196
let bsd_width = 5;
@@ -237,6 +282,7 @@ mod options {
237282
pub const FILE: &str = "file";
238283
pub const UNTAGGED: &str = "untagged";
239284
pub const LENGTH: &str = "length";
285+
pub const RAW: &str = "raw";
240286
}
241287

242288
#[uucore::main]
@@ -297,6 +343,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
297343
output_bits: bits,
298344
length,
299345
untagged: matches.get_flag(options::UNTAGGED),
346+
raw: matches.get_flag(options::RAW),
300347
};
301348

302349
match matches.get_many::<String>(options::FILE) {
@@ -353,5 +400,11 @@ pub fn uu_app() -> Command {
353400
.help("digest length in bits; must not exceed the max for the blake2 algorithm and must be a multiple of 8")
354401
.action(ArgAction::Set),
355402
)
403+
.arg(
404+
Arg::new(options::RAW)
405+
.long(options::RAW)
406+
.help("emit a raw binary digest, not hexadecimal")
407+
.action(ArgAction::SetTrue),
408+
)
356409
.after_help(AFTER_HELP)
357410
}

src/uucore/src/lib/features/format/argument.rs

Lines changed: 47 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6+
use crate::{
7+
error::set_exit_code,
8+
features::format::num_parser::{ParseError, ParsedNumber},
9+
quoting_style::{escape_name, Quotes, QuotingStyle},
10+
show_error, show_warning,
11+
};
612
use os_display::Quotable;
7-
8-
use crate::{error::set_exit_code, show_warning};
13+
use std::ffi::OsStr;
914

1015
/// An argument for formatting
1116
///
@@ -40,16 +45,7 @@ impl<'a, T: Iterator<Item = &'a FormatArgument>> ArgumentIter<'a> for T {
4045
};
4146
match next {
4247
FormatArgument::Char(c) => *c,
43-
FormatArgument::Unparsed(s) => {
44-
let mut chars = s.chars();
45-
let Some(c) = chars.next() else {
46-
return '\0';
47-
};
48-
let None = chars.next() else {
49-
return '\0';
50-
};
51-
c
52-
}
48+
FormatArgument::Unparsed(s) => s.chars().next().unwrap_or('\0'),
5349
_ => '\0',
5450
}
5551
}
@@ -60,25 +56,7 @@ impl<'a, T: Iterator<Item = &'a FormatArgument>> ArgumentIter<'a> for T {
6056
};
6157
match next {
6258
FormatArgument::UnsignedInt(n) => *n,
63-
FormatArgument::Unparsed(s) => {
64-
let opt = if let Some(s) = s.strip_prefix("0x") {
65-
u64::from_str_radix(s, 16).ok()
66-
} else if let Some(s) = s.strip_prefix('0') {
67-
u64::from_str_radix(s, 8).ok()
68-
} else if let Some(s) = s.strip_prefix('\'') {
69-
s.chars().next().map(|c| c as u64)
70-
} else {
71-
s.parse().ok()
72-
};
73-
match opt {
74-
Some(n) => n,
75-
None => {
76-
show_warning!("{}: expected a numeric value", s.quote());
77-
set_exit_code(1);
78-
0
79-
}
80-
}
81-
}
59+
FormatArgument::Unparsed(s) => extract_value(ParsedNumber::parse_u64(s), s),
8260
_ => 0,
8361
}
8462
}
@@ -89,29 +67,7 @@ impl<'a, T: Iterator<Item = &'a FormatArgument>> ArgumentIter<'a> for T {
8967
};
9068
match next {
9169
FormatArgument::SignedInt(n) => *n,
92-
FormatArgument::Unparsed(s) => {
93-
// For hex, we parse `u64` because we do not allow another
94-
// minus sign. We might need to do more precise parsing here.
95-
let opt = if let Some(s) = s.strip_prefix("-0x") {
96-
u64::from_str_radix(s, 16).ok().map(|x| -(x as i64))
97-
} else if let Some(s) = s.strip_prefix("0x") {
98-
u64::from_str_radix(s, 16).ok().map(|x| x as i64)
99-
} else if s.starts_with("-0") || s.starts_with('0') {
100-
i64::from_str_radix(s, 8).ok()
101-
} else if let Some(s) = s.strip_prefix('\'') {
102-
s.chars().next().map(|x| x as i64)
103-
} else {
104-
s.parse().ok()
105-
};
106-
match opt {
107-
Some(n) => n,
108-
None => {
109-
show_warning!("{}: expected a numeric value", s.quote());
110-
set_exit_code(1);
111-
0
112-
}
113-
}
114-
}
70+
FormatArgument::Unparsed(s) => extract_value(ParsedNumber::parse_i64(s), s),
11571
_ => 0,
11672
}
11773
}
@@ -122,23 +78,7 @@ impl<'a, T: Iterator<Item = &'a FormatArgument>> ArgumentIter<'a> for T {
12278
};
12379
match next {
12480
FormatArgument::Float(n) => *n,
125-
FormatArgument::Unparsed(s) => {
126-
let opt = if s.starts_with("0x") || s.starts_with("-0x") {
127-
unimplemented!("Hexadecimal floats are unimplemented!")
128-
} else if let Some(s) = s.strip_prefix('\'') {
129-
s.chars().next().map(|x| x as u64 as f64)
130-
} else {
131-
s.parse().ok()
132-
};
133-
match opt {
134-
Some(n) => n,
135-
None => {
136-
show_warning!("{}: expected a numeric value", s.quote());
137-
set_exit_code(1);
138-
0.0
139-
}
140-
}
141-
}
81+
FormatArgument::Unparsed(s) => extract_value(ParsedNumber::parse_f64(s), s),
14282
_ => 0.0,
14383
}
14484
}
@@ -150,3 +90,39 @@ impl<'a, T: Iterator<Item = &'a FormatArgument>> ArgumentIter<'a> for T {
15090
}
15191
}
15292
}
93+
94+
fn extract_value<T: Default>(p: Result<T, ParseError<'_, T>>, input: &str) -> T {
95+
match p {
96+
Ok(v) => v,
97+
Err(e) => {
98+
set_exit_code(1);
99+
let input = escape_name(
100+
OsStr::new(input),
101+
&QuotingStyle::C {
102+
quotes: Quotes::None,
103+
},
104+
);
105+
match e {
106+
ParseError::Overflow => {
107+
show_error!("{}: Numerical result out of range", input.quote());
108+
Default::default()
109+
}
110+
ParseError::NotNumeric => {
111+
show_error!("{}: expected a numeric value", input.quote());
112+
Default::default()
113+
}
114+
ParseError::PartialMatch(v, rest) => {
115+
if input.starts_with('\'') {
116+
show_warning!(
117+
"{}: character(s) following character constant have been ignored",
118+
&rest,
119+
);
120+
} else {
121+
show_error!("{}: value not completely converted", input.quote());
122+
}
123+
v
124+
}
125+
}
126+
}
127+
}
128+
}

src/uucore/src/lib/features/format/escape.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ pub fn parse_escape_code(rest: &mut &[u8]) -> EscapedChar {
108108
*rest = new_rest;
109109
match c {
110110
b'\\' => EscapedChar::Byte(b'\\'),
111+
b'"' => EscapedChar::Byte(b'"'),
111112
b'a' => EscapedChar::Byte(b'\x07'),
112113
b'b' => EscapedChar::Byte(b'\x08'),
113114
b'c' => EscapedChar::End,

src/uucore/src/lib/features/format/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
mod argument;
3434
mod escape;
3535
pub mod num_format;
36+
pub mod num_parser;
3637
mod spec;
3738

3839
pub use argument::*;

src/uucore/src/lib/features/format/num_format.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,17 @@ impl Formatter for UnsignedInt {
141141
let mut s = match self.variant {
142142
UnsignedIntVariant::Decimal => format!("{x}"),
143143
UnsignedIntVariant::Octal(Prefix::No) => format!("{x:o}"),
144-
UnsignedIntVariant::Octal(Prefix::Yes) => format!("{x:#o}"),
144+
UnsignedIntVariant::Octal(Prefix::Yes) => {
145+
// The prefix that rust uses is `0o`, but GNU uses `0`.
146+
// We also need to take into account that 0 should not be 00
147+
// Since this is an unsigned int, we do not need to take the minus
148+
// sign into account.
149+
if x != 0 {
150+
format!("0{x:o}")
151+
} else {
152+
format!("{x:o}")
153+
}
154+
}
145155
UnsignedIntVariant::Hexadecimal(Case::Lowercase, Prefix::No) => {
146156
format!("{x:x}")
147157
}
@@ -487,6 +497,27 @@ fn strip_fractional_zeroes_and_dot(s: &mut String) {
487497
mod test {
488498
use crate::format::num_format::{Case, ForceDecimal};
489499

500+
#[test]
501+
fn unsigned_octal() {
502+
use super::{Formatter, NumberAlignment, Prefix, UnsignedInt, UnsignedIntVariant};
503+
let f = |x| {
504+
let mut s = Vec::new();
505+
UnsignedInt {
506+
variant: UnsignedIntVariant::Octal(Prefix::Yes),
507+
width: 0,
508+
precision: 0,
509+
alignment: NumberAlignment::Left,
510+
}
511+
.fmt(&mut s, x)
512+
.unwrap();
513+
String::from_utf8(s).unwrap()
514+
};
515+
516+
assert_eq!(f(0), "0");
517+
assert_eq!(f(5), "05");
518+
assert_eq!(f(8), "010");
519+
}
520+
490521
#[test]
491522
fn decimal_float() {
492523
use super::format_float_decimal;

0 commit comments

Comments
 (0)