@@ -5,39 +5,91 @@ use std::io;
55
66use rustc_session:: EarlyErrorHandler ;
77
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+ #[ derive( Default ) ]
9+ struct Expander {
10+ shell_argfiles : bool ,
11+ next_is_unstable_option : bool ,
12+ expanded : Vec < String > ,
13+ }
14+
15+ impl Expander {
16+ fn arg ( & mut self , arg : & str ) -> Result < ( ) , Error > {
17+ if let Some ( argfile) = arg. strip_prefix ( '@' ) {
18+ match argfile. split_once ( ':' ) {
19+ Some ( ( "shell" , path) ) if self . shell_argfiles => {
20+ shlex:: split ( & Self :: read_file ( path) ?)
21+ . ok_or_else ( || Error :: ShellParseError ( path. to_string ( ) ) ) ?
22+ . into_iter ( )
23+ . for_each ( |arg| self . push ( arg) ) ;
24+ }
25+ _ => {
26+ let contents = Self :: read_file ( argfile) ?;
27+ contents. lines ( ) . for_each ( |arg| self . push ( arg. to_string ( ) ) ) ;
28+ }
29+ }
30+ } else {
31+ self . push ( arg. to_string ( ) ) ;
32+ }
33+
34+ Ok ( ( ) )
35+ }
36+
37+ fn push ( & mut self , arg : String ) {
38+ if self . next_is_unstable_option {
39+ self . inspect_unstable_option ( & arg) ;
40+ self . next_is_unstable_option = false ;
41+ } else if let Some ( unstable_option) = arg. strip_prefix ( "-Z" ) {
42+ if unstable_option. is_empty ( ) {
43+ self . next_is_unstable_option = true ;
44+ } else {
45+ self . inspect_unstable_option ( unstable_option) ;
46+ }
47+ }
48+
49+ self . expanded . push ( arg) ;
50+ }
51+
52+ fn finish ( self ) -> Vec < String > {
53+ self . expanded
54+ }
55+
56+ /// Parses any unstable flags specified on the command line.
57+ fn inspect_unstable_option ( & mut self , option : & str ) {
58+ match option {
59+ "shell-argfiles" => self . shell_argfiles = true ,
60+ _ => ( ) ,
61+ }
62+ }
63+
64+ fn read_file ( path : & str ) -> Result < String , Error > {
65+ fs:: read_to_string ( path) . map_err ( |e| {
66+ if e. kind ( ) == io:: ErrorKind :: InvalidData {
67+ Error :: Utf8Error ( Some ( path. to_string ( ) ) )
68+ } else {
69+ Error :: IOError ( path. to_string ( ) , e)
1470 }
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] )
71+ } )
2072 }
2173}
2274
2375/// **Note:** This function doesn't interpret argument 0 in any special way.
2476/// If this function is intended to be used with command line arguments,
2577/// `argv[0]` must be removed prior to calling it manually.
2678pub fn arg_expand_all ( handler : & EarlyErrorHandler , at_args : & [ String ] ) -> Vec < String > {
27- let mut args = Vec :: new ( ) ;
79+ let mut expander = Expander :: default ( ) ;
2880 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}" ) ) ,
81+ if let Err ( err) = expander. arg ( arg) {
82+ handler. early_error ( format ! ( "Failed to load argument file: {err}" ) ) ;
3283 }
3384 }
34- args
85+ expander . finish ( )
3586}
3687
3788#[ derive( Debug ) ]
3889pub enum Error {
3990 Utf8Error ( Option < String > ) ,
4091 IOError ( String , io:: Error ) ,
92+ ShellParseError ( String ) ,
4193}
4294
4395impl fmt:: Display for Error {
@@ -46,6 +98,7 @@ impl fmt::Display for Error {
4698 Error :: Utf8Error ( None ) => write ! ( fmt, "Utf8 error" ) ,
4799 Error :: Utf8Error ( Some ( path) ) => write ! ( fmt, "Utf8 error in {path}" ) ,
48100 Error :: IOError ( path, err) => write ! ( fmt, "IO Error: {path}: {err}" ) ,
101+ Error :: ShellParseError ( path) => write ! ( fmt, "Invalid shell-style arguments in {path}" ) ,
49102 }
50103 }
51104}
0 commit comments