Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitafbe90f

Browse files
authored
Merge pull request#8310 from RGBCube/echo-help
echo: print help if not posixly correct and only argument is --help
2 parentsdddcf2a +3ea679a commitafbe90f

File tree

2 files changed

+139
-106
lines changed

2 files changed

+139
-106
lines changed

‎src/uu/echo/src/echo.rs

Lines changed: 133 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::io::{self, StdoutLock, Write};
1111
use uucore::error::{UResult,USimpleError};
1212
use uucore::format::{FormatChar,OctalParsing, parse_escape_only};
1313
use uucore::format_usage;
14+
use uucore::os_str_as_bytes;
1415

1516
use uucore::locale::get_message;
1617

@@ -21,109 +22,157 @@ mod options {
2122
pubconstDISABLE_BACKSLASH_ESCAPE:&str ="disable_backslash_escape";
2223
}
2324

24-
///Holds the optionsfor echo command:
25-
/// -n (disable newline)
26-
/// -e/-E (escape handling),
27-
structEchoOptions{
28-
/// -n flag option: if true, output a trailing newline (-n disables it)
29-
///Default: true
25+
///Optionsfortheecho command.
26+
#[derive(Debug,Clone,Copy)]
27+
structOptions{
28+
/// Whether the output should have a trailing newline.
29+
///
30+
///True by default. `-n` disables it.
3031
pubtrailing_newline:bool,
3132

32-
/// -e enables escape interpretation, -E disables it
33-
/// Default: false (escape interpretation disabled)
33+
/// Whether given string literals should be parsed for
34+
/// escape characters.
35+
///
36+
/// False by default, can be enabled with `-e`. Always true if
37+
/// `POSIXLY_CORRECT` (cannot be disabled with `-E`).
3438
pubescape:bool,
3539
}
3640

37-
/// Checks if an argument is a valid echo flag
38-
/// Returns true if valid echo flag found
39-
fnis_echo_flag(arg:&OsString,echo_options:&mutEchoOptions) ->bool{
40-
let bytes = arg.as_encoded_bytes();
41-
if bytes.first() ==Some(&b'-') && arg !="-"{
42-
// we initialize our local variables to the "current" options so we don't override
43-
// previous found flags
44-
letmut escape = echo_options.escape;
45-
letmut trailing_newline = echo_options.trailing_newline;
46-
47-
// Process characters after the '-'
48-
for cin&bytes[1..]{
49-
match c{
50-
b'e' => escape =true,
51-
b'E' => escape =false,
52-
b'n' => trailing_newline =false,
53-
// if there is any char in an argument starting with '-' that doesn't match e/E/n
54-
// present means that this argument is not a flag
55-
_ =>returnfalse,
56-
}
41+
implDefaultforOptions{
42+
fndefault() ->Self{
43+
Self{
44+
trailing_newline:true,
45+
escape:false,
46+
}
47+
}
48+
}
49+
50+
implOptions{
51+
fnposixly_correct_default() ->Self{
52+
Self{
53+
trailing_newline:true,
54+
escape:true,
5755
}
56+
}
57+
}
58+
59+
/// Checks if an argument is a valid echo flag, and if
60+
/// it is records the changes in [`Options`].
61+
fnis_flag(arg:&OsStr,options:&mutOptions) ->bool{
62+
let arg = arg.as_encoded_bytes();
63+
64+
if arg.first() !=Some(&b'-') || arg ==b"-"{
65+
// Argument doesn't start with '-' or is '-' => not a flag.
66+
returnfalse;
67+
}
5868

59-
// we only override the options with flags being found once we parsed the whole argument
60-
echo_options.escape = escape;
61-
echo_options.trailing_newline = trailing_newline;
62-
returntrue;
69+
// We don't modify the given options until after
70+
// the loop because there is a chance the flag isn't
71+
// valid after all & shouldn't affect the options.
72+
letmut options_:Options =*options;
73+
74+
// Skip the '-' when processing characters.
75+
for cin&arg[1..]{
76+
match c{
77+
b'e' => options_.escape =true,
78+
b'E' => options_.escape =false,
79+
b'n' => options_.trailing_newline =false,
80+
81+
// If there is any character in an supposed flag
82+
// that is not a valid flag character, it is not
83+
// a flag.
84+
//
85+
// "-eeEnEe" => is a flag.
86+
// "-eeBne" => not a flag, short circuit at the B.
87+
_ =>returnfalse,
88+
}
6389
}
6490

65-
// argument doesn't start with '-' or is "-" => no flag
66-
false
91+
// We are now sure that the argument is a
92+
// flag, and can apply the modified options.
93+
*options = options_;
94+
true
6795
}
6896

69-
/// Processes command line arguments, separating flags from normal arguments
70-
/// Returns:
71-
/// - Vector of non-flag arguments
72-
/// - `trailing_newline`: whether to print a trailing newline
73-
/// - `escape`: whether to process escape sequences
74-
fnfilter_echo_flags(args:impl uucore::Args) ->(Vec<OsString>,bool,bool){
75-
letmut result =Vec::new();
76-
letmut echo_options =EchoOptions{
77-
trailing_newline:true,
78-
escape:false,
79-
};
80-
letmut args_iter = args.into_iter();
81-
82-
// Process arguments until first non-flag is found
83-
for argin&mut args_iter{
84-
// we parse flags and store options found in "echo_option". First is_echo_flag
85-
// call to return false will break the loop and we will collect the remaining arguments
86-
if !is_echo_flag(&arg,&mut echo_options){
87-
// First non-flag argument stops flag processing
88-
result.push(arg);
97+
/// Processes command line arguments, separating flags from normal arguments.
98+
///
99+
/// # Returns
100+
///
101+
/// - Vector of non-flag arguments.
102+
/// - [`Options`], describing how teh arguments should be interpreted.
103+
fnfilter_flags(mutargs:implIterator<Item =OsString>) ->(Vec<OsString>,Options){
104+
letmut arguments =Vec::with_capacity(args.size_hint().0);
105+
letmut options =Options::default();
106+
107+
// Process arguments until first non-flag is found.
108+
for argin&mut args{
109+
// We parse flags and aggregate the options in `options`.
110+
// First call to `is_echo_flag` to return false will break the loop.
111+
if !is_flag(&arg,&mut options){
112+
// Not a flag. Can break out of flag-processing loop.
113+
// Don't forget to push it to the arguments too.
114+
arguments.push(arg);
89115
break;
90116
}
91117
}
92-
// Collect remaining arguments
93-
for argin args_iter{
94-
result.push(arg);
95-
}
96-
(result, echo_options.trailing_newline, echo_options.escape)
118+
119+
// Collect remaining non-flag arguments.
120+
arguments.extend(args);
121+
122+
(arguments, options)
97123
}
98124

99125
#[uucore::main]
100126
pubfnuumain(args:impl uucore::Args) ->UResult<()>{
127+
// args[0] is the name of the binary.
128+
let args:Vec<OsString> = args.skip(1).collect();
129+
101130
// Check POSIX compatibility mode
131+
//
132+
// From the GNU manual, on what it should do:
133+
//
134+
// > If the POSIXLY_CORRECT environment variable is set, then when
135+
// > echo’s first argument is not -n it outputs option-like arguments
136+
// > instead of treating them as options. For example, echo -ne hello
137+
// > outputs ‘-ne hello’ instead of plain ‘hello’. Also backslash
138+
// > escapes are always enabled. To echo the string ‘-n’, one of the
139+
// > characters can be escaped in either octal or hexadecimal
140+
// > representation. For example, echo -e '\x2dn'.
102141
let is_posixly_correct = env::var_os("POSIXLY_CORRECT").is_some();
103142

104-
let args_iter = args.skip(1);
105-
let(args, trailing_newline, escaped) =if is_posixly_correct{
106-
letmut args_iter = args_iter.peekable();
107-
108-
if args_iter.peek() ==Some(&OsString::from("-n")){
143+
let(args, options) =if is_posixly_correct{
144+
if args.first().is_some_and(|arg| arg =="-n"){
109145
// if POSIXLY_CORRECT is set and the first argument is the "-n" flag
110-
// we filter flags normally but 'escaped' is activated nonetheless
111-
let(args, _, _) =filter_echo_flags(args_iter);
112-
(args,false,true)
146+
// we filter flags normally but 'escaped' is activated nonetheless.
147+
let(args, _) =filter_flags(args.into_iter());
148+
(
149+
args,
150+
Options{
151+
trailing_newline:false,
152+
..Options::posixly_correct_default()
153+
},
154+
)
113155
}else{
114156
// if POSIXLY_CORRECT is set and the first argument is not the "-n" flag
115-
// we just collect all arguments as every argument is considered an argument
116-
let args:Vec<OsString> = args_iter.collect();
117-
(args,true,true)
157+
// we just collect all arguments as no arguments are interpreted as flags.
158+
(args,Options::posixly_correct_default())
118159
}
160+
}elseif args.len() ==1 && args[0] =="--help"{
161+
// If POSIXLY_CORRECT is not set and the first argument
162+
// is `--help`, GNU coreutils prints the help message.
163+
//
164+
// Verify this using:
165+
//
166+
// POSIXLY_CORRECT=1 echo --help
167+
// echo --help
168+
uu_app().print_help()?;
169+
returnOk(());
119170
}else{
120171
// if POSIXLY_CORRECT is not set we filter the flags normally
121-
let(args, trailing_newline, escaped) =filter_echo_flags(args_iter);
122-
(args, trailing_newline, escaped)
172+
filter_flags(args.into_iter())
123173
};
124174

125-
letmut stdout_lock = io::stdout().lock();
126-
execute(&mut stdout_lock, args, trailing_newline, escaped)?;
175+
execute(&mut io::stdout().lock(), args, options)?;
127176

128177
Ok(())
129178
}
@@ -169,51 +218,29 @@ pub fn uu_app() -> Command {
169218
)
170219
}
171220

172-
fnexecute(
173-
stdout_lock:&mutStdoutLock,
174-
arguments_after_options:Vec<OsString>,
175-
trailing_newline:bool,
176-
escaped:bool,
177-
) ->UResult<()>{
178-
for(i, input)in arguments_after_options.into_iter().enumerate(){
179-
letSome(bytes) =bytes_from_os_string(input.as_os_str())else{
180-
returnErr(USimpleError::new(1,get_message("echo-error-non-utf8")));
181-
};
221+
fnexecute(stdout:&mutStdoutLock,args:Vec<OsString>,options:Options) ->UResult<()>{
222+
for(i, arg)in args.into_iter().enumerate(){
223+
let bytes =os_str_as_bytes(arg.as_os_str())
224+
.map_err(|_|USimpleError::new(1,get_message("echo-error-non-utf8")))?;
182225

183226
if i >0{
184-
stdout_lock.write_all(b" ")?;
227+
stdout.write_all(b" ")?;
185228
}
186229

187-
ifescaped{
230+
ifoptions.escape{
188231
for iteminparse_escape_only(bytes,OctalParsing::ThreeDigits){
189-
if item.write(&mut*stdout_lock)?.is_break(){
232+
if item.write(&mut*stdout)?.is_break(){
190233
returnOk(());
191234
}
192235
}
193236
}else{
194-
stdout_lock.write_all(bytes)?;
237+
stdout.write_all(bytes)?;
195238
}
196239
}
197240

198-
if trailing_newline{
199-
stdout_lock.write_all(b"\n")?;
241+
ifoptions.trailing_newline{
242+
stdout.write_all(b"\n")?;
200243
}
201244

202245
Ok(())
203246
}
204-
205-
fnbytes_from_os_string(input:&OsStr) ->Option<&[u8]>{
206-
#[cfg(target_family ="unix")]
207-
{
208-
use std::os::unix::ffi::OsStrExt;
209-
210-
Some(input.as_bytes())
211-
}
212-
213-
#[cfg(not(target_family ="unix"))]
214-
{
215-
// TODO
216-
// Verify that this works correctly on these platforms
217-
input.to_str().map(|st| st.as_bytes())
218-
}
219-
}

‎tests/by-util/test_echo.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,12 @@ fn partial_help_argument() {
514514
new_ucmd!().arg("--he").succeeds().stdout_is("--he\n");
515515
}
516516

517+
#[test]
518+
fnonly_help_argument_prints_help(){
519+
assert_ne!(new_ucmd!().arg("--help").succeeds().stdout(),b"--help\n");
520+
assert_ne!(new_ucmd!().arg("--help").succeeds().stdout(),b"--help");// This one is just in case.
521+
}
522+
517523
#[test]
518524
fnmultibyte_escape_unicode(){
519525
// spell-checker:disable-next-line

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp