@@ -5,39 +5,97 @@ use std::io;
5
5
6
6
use rustc_session:: EarlyErrorHandler ;
7
7
8
- fn arg_expand ( arg : String ) -> Result < Vec < String > , Error > {
9
- if let Some ( path) = arg. strip_prefix ( '@' ) {
10
- let file = match fs:: read_to_string ( path) {
11
- Ok ( file) => file,
12
- Err ( ref err) if err. kind ( ) == io:: ErrorKind :: InvalidData => {
13
- return Err ( Error :: Utf8Error ( Some ( path. to_string ( ) ) ) ) ;
8
+ /// Expands argfiles in command line arguments.
9
+ #[ derive( Default ) ]
10
+ struct Expander {
11
+ shell_argfiles : bool ,
12
+ next_is_unstable_option : bool ,
13
+ expanded : Vec < String > ,
14
+ }
15
+
16
+ impl Expander {
17
+ /// Handles the next argument. If the argument is an argfile, it is expanded
18
+ /// inline.
19
+ fn arg ( & mut self , arg : & str ) -> Result < ( ) , Error > {
20
+ if let Some ( argfile) = arg. strip_prefix ( '@' ) {
21
+ match argfile. split_once ( ':' ) {
22
+ Some ( ( "shell" , path) ) if self . shell_argfiles => {
23
+ shlex:: split ( & Self :: read_file ( path) ?)
24
+ . ok_or_else ( || Error :: ShellParseError ( path. to_string ( ) ) ) ?
25
+ . into_iter ( )
26
+ . for_each ( |arg| self . push ( arg) ) ;
27
+ }
28
+ _ => {
29
+ let contents = Self :: read_file ( argfile) ?;
30
+ contents. lines ( ) . for_each ( |arg| self . push ( arg. to_string ( ) ) ) ;
31
+ }
32
+ }
33
+ } else {
34
+ self . push ( arg. to_string ( ) ) ;
35
+ }
36
+
37
+ Ok ( ( ) )
38
+ }
39
+
40
+ /// Adds a command line argument verbatim with no argfile expansion.
41
+ fn push ( & mut self , arg : String ) {
42
+ if self . next_is_unstable_option {
43
+ self . inspect_unstable_option ( & arg) ;
44
+ self . next_is_unstable_option = false ;
45
+ } else if let Some ( unstable_option) = arg. strip_prefix ( "-Z" ) {
46
+ if unstable_option. is_empty ( ) {
47
+ self . next_is_unstable_option = true ;
48
+ } else {
49
+ self . inspect_unstable_option ( unstable_option) ;
50
+ }
51
+ }
52
+
53
+ self . expanded . push ( arg) ;
54
+ }
55
+
56
+ /// Consumes the `Expander`, returning the expanded arguments.
57
+ fn finish ( self ) -> Vec < String > {
58
+ self . expanded
59
+ }
60
+
61
+ /// Parses any relevant unstable flags specified on the command line.
62
+ fn inspect_unstable_option ( & mut self , option : & str ) {
63
+ match option {
64
+ "shell-argfiles" => self . shell_argfiles = true ,
65
+ _ => ( ) ,
66
+ }
67
+ }
68
+
69
+ /// Reads the contents of a file as UTF-8.
70
+ fn read_file ( path : & str ) -> Result < String , Error > {
71
+ fs:: read_to_string ( path) . map_err ( |e| {
72
+ if e. kind ( ) == io:: ErrorKind :: InvalidData {
73
+ Error :: Utf8Error ( Some ( path. to_string ( ) ) )
74
+ } else {
75
+ Error :: IOError ( path. to_string ( ) , e)
14
76
}
15
- Err ( err) => return Err ( Error :: IOError ( path. to_string ( ) , err) ) ,
16
- } ;
17
- Ok ( file. lines ( ) . map ( ToString :: to_string) . collect ( ) )
18
- } else {
19
- Ok ( vec ! [ arg] )
77
+ } )
20
78
}
21
79
}
22
80
23
81
/// **Note:** This function doesn't interpret argument 0 in any special way.
24
82
/// If this function is intended to be used with command line arguments,
25
83
/// `argv[0]` must be removed prior to calling it manually.
26
84
pub fn arg_expand_all ( handler : & EarlyErrorHandler , at_args : & [ String ] ) -> Vec < String > {
27
- let mut args = Vec :: new ( ) ;
85
+ let mut expander = Expander :: default ( ) ;
28
86
for arg in at_args {
29
- match arg_expand ( arg. clone ( ) ) {
30
- Ok ( arg) => args. extend ( arg) ,
31
- Err ( err) => handler. early_error ( format ! ( "Failed to load argument file: {err}" ) ) ,
87
+ if let Err ( err) = expander. arg ( arg) {
88
+ handler. early_error ( format ! ( "Failed to load argument file: {err}" ) ) ;
32
89
}
33
90
}
34
- args
91
+ expander . finish ( )
35
92
}
36
93
37
94
#[ derive( Debug ) ]
38
95
pub enum Error {
39
96
Utf8Error ( Option < String > ) ,
40
97
IOError ( String , io:: Error ) ,
98
+ ShellParseError ( String ) ,
41
99
}
42
100
43
101
impl fmt:: Display for Error {
@@ -46,6 +104,7 @@ impl fmt::Display for Error {
46
104
Error :: Utf8Error ( None ) => write ! ( fmt, "Utf8 error" ) ,
47
105
Error :: Utf8Error ( Some ( path) ) => write ! ( fmt, "Utf8 error in {path}" ) ,
48
106
Error :: IOError ( path, err) => write ! ( fmt, "IO Error: {path}: {err}" ) ,
107
+ Error :: ShellParseError ( path) => write ! ( fmt, "Invalid shell-style arguments in {path}" ) ,
49
108
}
50
109
}
51
110
}
0 commit comments