|
1 | 1 | use std::collections::HashMap;
|
2 | 2 | use std::env;
|
3 | 3 | use std::fmt;
|
| 4 | +use std::fs::File; |
| 5 | +use std::io::{self, Write}; |
4 | 6 | use std::process::exit;
|
5 | 7 |
|
6 | 8 | use crate::flags::{FlagParseError, Flags, ParseOutcome};
|
@@ -185,7 +187,7 @@ pub(crate) fn options() -> Result<Options, OptionError> {
|
185 | 187 | );
|
186 | 188 | // Append all the arguments fetched from files to those provided via command line.
|
187 | 189 | child_args.append(&mut file_arguments);
|
188 |
| - let child_args = prepare_args(child_args, &subst_mappings); |
| 190 | + let child_args = prepare_args(child_args, &subst_mappings)?; |
189 | 191 | // Split the executable path from the rest of the arguments.
|
190 | 192 | let (exec_path, args) = child_args.split_first().ok_or_else(|| {
|
191 | 193 | OptionError::Generic(
|
@@ -234,15 +236,66 @@ fn env_from_files(paths: Vec<String>) -> Result<HashMap<String, String>, OptionE
|
234 | 236 | Ok(env_vars)
|
235 | 237 | }
|
236 | 238 |
|
237 |
| -fn prepare_args(mut args: Vec<String>, subst_mappings: &[(String, String)]) -> Vec<String> { |
| 239 | +fn prepare_arg(mut arg: String, subst_mappings: &[(String, String)]) -> String { |
238 | 240 | for (f, replace_with) in subst_mappings {
|
239 |
| - for arg in args.iter_mut() { |
240 |
| - let from = format!("${{{}}}", f); |
241 |
| - let new = arg.replace(from.as_str(), replace_with); |
242 |
| - *arg = new; |
| 241 | + let from = format!("${{{}}}", f); |
| 242 | + arg = arg.replace(&from, replace_with); |
| 243 | + } |
| 244 | + arg |
| 245 | +} |
| 246 | + |
| 247 | +/// Apply substitutions to the given param file. Returns the new filename. |
| 248 | +fn prepare_param_file( |
| 249 | + filename: &str, |
| 250 | + subst_mappings: &[(String, String)], |
| 251 | +) -> Result<String, OptionError> { |
| 252 | + let expanded_file = format!("{}.expanded", filename); |
| 253 | + let format_err = |err: io::Error| { |
| 254 | + OptionError::Generic(format!( |
| 255 | + "{} writing path: {:?}, current directory: {:?}", |
| 256 | + err, |
| 257 | + expanded_file, |
| 258 | + std::env::current_dir() |
| 259 | + )) |
| 260 | + }; |
| 261 | + let mut out = io::BufWriter::new(File::create(&expanded_file).map_err(&format_err)?); |
| 262 | + fn process_file( |
| 263 | + filename: &str, |
| 264 | + out: &mut io::BufWriter<File>, |
| 265 | + subst_mappings: &[(String, String)], |
| 266 | + format_err: &impl Fn(io::Error) -> OptionError, |
| 267 | + ) -> Result<(), OptionError> { |
| 268 | + for arg in read_file_to_array(filename).map_err(OptionError::Generic)? { |
| 269 | + let arg = prepare_arg(arg, subst_mappings); |
| 270 | + if let Some(arg_file) = arg.strip_prefix('@') { |
| 271 | + process_file(arg_file, out, subst_mappings, format_err)?; |
| 272 | + } else { |
| 273 | + writeln!(out, "{}", arg).map_err(format_err)?; |
| 274 | + } |
243 | 275 | }
|
| 276 | + Ok(()) |
244 | 277 | }
|
245 |
| - args |
| 278 | + process_file(filename, &mut out, subst_mappings, &format_err)?; |
| 279 | + Ok(expanded_file) |
| 280 | +} |
| 281 | + |
| 282 | +/// Apply substitutions to the provided arguments, recursing into param files. |
| 283 | +fn prepare_args( |
| 284 | + args: Vec<String>, |
| 285 | + subst_mappings: &[(String, String)], |
| 286 | +) -> Result<Vec<String>, OptionError> { |
| 287 | + args.into_iter() |
| 288 | + .map(|arg| { |
| 289 | + let arg = prepare_arg(arg, subst_mappings); |
| 290 | + if let Some(param_file) = arg.strip_prefix('@') { |
| 291 | + // Note that substitutions may also apply to the param file path! |
| 292 | + prepare_param_file(param_file, subst_mappings) |
| 293 | + .map(|filename| format!("@{}", filename)) |
| 294 | + } else { |
| 295 | + Ok(arg) |
| 296 | + } |
| 297 | + }) |
| 298 | + .collect() |
246 | 299 | }
|
247 | 300 |
|
248 | 301 | fn environment_block(
|
|
0 commit comments