4
4
// file that was distributed with this source code.
5
5
6
6
use clap:: builder:: ValueParser ;
7
- use clap:: { Arg , ArgAction , ArgMatches , Command } ;
7
+ use clap:: { Arg , ArgAction , Command } ;
8
8
use std:: env;
9
9
use std:: ffi:: { OsStr , OsString } ;
10
10
use std:: io:: { self , StdoutLock , Write } ;
@@ -23,63 +23,104 @@ mod options {
23
23
pub const DISABLE_BACKSLASH_ESCAPE : & str = "disable_backslash_escape" ;
24
24
}
25
25
26
- fn is_echo_flag ( arg : & OsString ) -> bool {
27
- matches ! ( arg. to_str( ) , Some ( "-e" | "-E" | "-n" ) )
26
+ /// Holds the options for echo command:
27
+ /// -n (disable newline)
28
+ /// -e/-E (escape handling),
29
+ struct EchoOptions {
30
+ /// -n flag option: if true, output a trailing newline (-n disables it)
31
+ /// Default: true
32
+ pub trailing_newline : bool ,
33
+
34
+ /// -e enables escape interpretation, -E disables it
35
+ /// Default: false (escape interpretation disabled)
36
+ pub escape : bool ,
28
37
}
29
38
30
- // A workaround because clap interprets the first '--' as a marker that a value
31
- // follows. In order to use '--' as a value, we have to inject an additional '--'
32
- fn handle_double_hyphens ( args : impl uucore :: Args ) -> impl uucore :: Args {
33
- let mut result = Vec :: new ( ) ;
34
- let mut is_first_argument = true ;
35
- let mut args_iter = args . into_iter ( ) ;
36
-
37
- if let Some ( first_val ) = args_iter . next ( ) {
38
- // the first argument ('echo') gets pushed before we start with the checks for flags/'--'
39
- result . push ( first_val ) ;
40
- // We need to skip any possible Flag arguments until we find the first argument to echo that
41
- // is not a flag. If the first argument is double hyphen we inject an additional '--'
42
- // otherwise we switch is_first_argument boolean to skip the checks for any further arguments
43
- for arg in args_iter {
44
- if is_first_argument && ! is_echo_flag ( & arg ) {
45
- is_first_argument = false ;
46
- if arg == "--" {
47
- result . push ( OsString :: from ( "--" ) ) ;
48
- }
39
+ /// Checks if an argument is a valid echo flag
40
+ /// Returns true if valid echo flag found
41
+ fn is_echo_flag ( arg : & OsString , echo_options : & mut EchoOptions ) -> bool {
42
+ let bytes = arg . as_encoded_bytes ( ) ;
43
+ if bytes . first ( ) == Some ( & b'-' ) && arg != "-" {
44
+ // we initialize our local variables to the "current" options so we don't override
45
+ // previous found flags
46
+ let mut escape = echo_options . escape ;
47
+ let mut trailing_newline = echo_options . trailing_newline ;
48
+
49
+ // Process characters after the '-'
50
+ for c in & bytes [ 1 .. ] {
51
+ match c {
52
+ b'e' => escape = true ,
53
+ b'E' => escape = false ,
54
+ b'n' => trailing_newline = false ,
55
+ // if there is any char in an argument starting with '-' that doesn't match e/E/n
56
+ // present means that this argument is not a flag
57
+ _ => return false ,
49
58
}
50
- result. push ( arg) ;
51
59
}
60
+
61
+ // we only override the options with flags being found once we parsed the whole argument
62
+ echo_options. escape = escape;
63
+ echo_options. trailing_newline = trailing_newline;
64
+ return true ;
52
65
}
53
66
54
- result. into_iter ( )
67
+ // argument doesn't start with '-' or is "-" => no flag
68
+ false
55
69
}
56
70
57
- fn collect_args ( matches : & ArgMatches ) -> Vec < OsString > {
58
- matches
59
- . get_many :: < OsString > ( options:: STRING )
60
- . map_or_else ( Vec :: new, |values| values. cloned ( ) . collect ( ) )
71
+ /// Processes command line arguments, separating flags from normal arguments
72
+ /// Returns:
73
+ /// - Vector of non-flag arguments
74
+ /// - trailing_newline: whether to print a trailing newline
75
+ /// - escape: whether to process escape sequences
76
+ fn filter_echo_flags ( args : impl uucore:: Args ) -> ( Vec < OsString > , bool , bool ) {
77
+ let mut result = Vec :: new ( ) ;
78
+ let mut echo_options = EchoOptions {
79
+ trailing_newline : true ,
80
+ escape : false ,
81
+ } ;
82
+ let mut args_iter = args. into_iter ( ) ;
83
+
84
+ // Process arguments until first non-flag is found
85
+ for arg in & mut args_iter {
86
+ // we parse flags and store options found in "echo_option". First is_echo_flag
87
+ // call to return false will break the loop and we will collect the remaining arguments
88
+ if !is_echo_flag ( & arg, & mut echo_options) {
89
+ // First non-flag argument stops flag processing
90
+ result. push ( arg) ;
91
+ break ;
92
+ }
93
+ }
94
+ // Collect remaining arguments
95
+ for arg in args_iter {
96
+ result. push ( arg) ;
97
+ }
98
+ ( result, echo_options. trailing_newline , echo_options. escape )
61
99
}
62
100
63
101
#[ uucore:: main]
64
102
pub fn uumain ( args : impl uucore:: Args ) -> UResult < ( ) > {
65
- let is_posixly_correct = env:: var ( "POSIXLY_CORRECT" ) . is_ok ( ) ;
103
+ // Check POSIX compatibility mode
104
+ let is_posixly_correct = env:: var_os ( "POSIXLY_CORRECT" ) . is_some ( ) ;
66
105
106
+ let args_iter = args. skip ( 1 ) ;
67
107
let ( args, trailing_newline, escaped) = if is_posixly_correct {
68
- let mut args_iter = args . skip ( 1 ) . peekable ( ) ;
108
+ let mut args_iter = args_iter . peekable ( ) ;
69
109
70
110
if args_iter. peek ( ) == Some ( & OsString :: from ( "-n" ) ) {
71
- let matches = uu_app ( ) . get_matches_from ( handle_double_hyphens ( args_iter) ) ;
72
- let args = collect_args ( & matches) ;
111
+ // if POSIXLY_CORRECT is set and the first argument is the "-n" flag
112
+ // we filter flags normally but 'escaped' is activated nonetheless
113
+ let ( args, _, _) = filter_echo_flags ( args_iter) ;
73
114
( args, false , true )
74
115
} else {
75
- let args: Vec < _ > = args_iter. collect ( ) ;
116
+ // if POSIXLY_CORRECT is set and the first argument is not the "-n" flag
117
+ // we just collect all arguments as every argument is considered an argument
118
+ let args: Vec < OsString > = args_iter. collect ( ) ;
76
119
( args, true , true )
77
120
}
78
121
} else {
79
- let matches = uu_app ( ) . get_matches_from ( handle_double_hyphens ( args. into_iter ( ) ) ) ;
80
- let trailing_newline = !matches. get_flag ( options:: NO_NEWLINE ) ;
81
- let escaped = matches. get_flag ( options:: ENABLE_BACKSLASH_ESCAPE ) ;
82
- let args = collect_args ( & matches) ;
122
+ // if POSIXLY_CORRECT is not set we filter the flags normally
123
+ let ( args, trailing_newline, escaped) = filter_echo_flags ( args_iter) ;
83
124
( args, trailing_newline, escaped)
84
125
} ;
85
126
0 commit comments