diff options
| author | Rob Mensching <rob@firegiant.com> | 2018-07-12 22:27:09 -0700 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2018-07-12 22:38:12 -0700 |
| commit | fc92b28f87599ac25d35399dc2df2f356a285960 (patch) | |
| tree | 0a775850ec5b4ff580b949700b51f5eee3182325 /src/WixToolset.Core/CommandLine/CommandLineParser.cs | |
| parent | 1a2d7994764060dc6f8936fab1c03e255f2671c5 (diff) | |
| download | wix-fc92b28f87599ac25d35399dc2df2f356a285960.tar.gz wix-fc92b28f87599ac25d35399dc2df2f356a285960.tar.bz2 wix-fc92b28f87599ac25d35399dc2df2f356a285960.zip | |
Refactor command line parsing to enable extensions there in light.exe
Fixes wixtoolset/issues#5845
Diffstat (limited to 'src/WixToolset.Core/CommandLine/CommandLineParser.cs')
| -rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLineParser.cs | 593 |
1 files changed, 165 insertions, 428 deletions
diff --git a/src/WixToolset.Core/CommandLine/CommandLineParser.cs b/src/WixToolset.Core/CommandLine/CommandLineParser.cs index f4bc8ade..da0e979c 100644 --- a/src/WixToolset.Core/CommandLine/CommandLineParser.cs +++ b/src/WixToolset.Core/CommandLine/CommandLineParser.cs | |||
| @@ -5,9 +5,6 @@ namespace WixToolset.Core.CommandLine | |||
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.IO; | 7 | using System.IO; |
| 8 | using System.Linq; | ||
| 9 | using System.Text; | ||
| 10 | using System.Text.RegularExpressions; | ||
| 11 | using WixToolset.Data; | 8 | using WixToolset.Data; |
| 12 | using WixToolset.Extensibility; | 9 | using WixToolset.Extensibility; |
| 13 | using WixToolset.Extensibility.Services; | 10 | using WixToolset.Extensibility.Services; |
| @@ -22,7 +19,7 @@ namespace WixToolset.Core.CommandLine | |||
| 22 | Bind, | 19 | Bind, |
| 23 | } | 20 | } |
| 24 | 21 | ||
| 25 | internal class CommandLineParser : ICommandLine, IParseCommandLine | 22 | internal class CommandLineParser : ICommandLine |
| 26 | { | 23 | { |
| 27 | private IServiceProvider ServiceProvider { get; set; } | 24 | private IServiceProvider ServiceProvider { get; set; } |
| 28 | 25 | ||
| @@ -32,14 +29,8 @@ namespace WixToolset.Core.CommandLine | |||
| 32 | 29 | ||
| 33 | public string ActiveCommand { get; private set; } | 30 | public string ActiveCommand { get; private set; } |
| 34 | 31 | ||
| 35 | public string[] OriginalArguments { get; private set; } | ||
| 36 | |||
| 37 | public Queue<string> RemainingArguments { get; } = new Queue<string>(); | ||
| 38 | |||
| 39 | public IExtensionManager ExtensionManager { get; private set; } | 32 | public IExtensionManager ExtensionManager { get; private set; } |
| 40 | 33 | ||
| 41 | public string ErrorArgument { get; set; } | ||
| 42 | |||
| 43 | public bool ShowHelp { get; set; } | 34 | public bool ShowHelp { get; set; } |
| 44 | 35 | ||
| 45 | public ICommandLineCommand ParseStandardCommandLine(ICommandLineContext context) | 36 | public ICommandLineCommand ParseStandardCommandLine(ICommandLineContext context) |
| @@ -50,18 +41,6 @@ namespace WixToolset.Core.CommandLine | |||
| 50 | 41 | ||
| 51 | this.ExtensionManager = context.ExtensionManager ?? this.ServiceProvider.GetService<IExtensionManager>(); | 42 | this.ExtensionManager = context.ExtensionManager ?? this.ServiceProvider.GetService<IExtensionManager>(); |
| 52 | 43 | ||
| 53 | var args = context.ParsedArguments ?? Array.Empty<string>(); | ||
| 54 | |||
| 55 | if (!String.IsNullOrEmpty(context.Arguments)) | ||
| 56 | { | ||
| 57 | args = CommandLineParser.ParseArgumentsToArray(context.Arguments).Concat(args).ToArray(); | ||
| 58 | } | ||
| 59 | |||
| 60 | return this.ParseStandardCommandLine(context, args); | ||
| 61 | } | ||
| 62 | |||
| 63 | private ICommandLineCommand ParseStandardCommandLine(ICommandLineContext context, string[] args) | ||
| 64 | { | ||
| 65 | var next = String.Empty; | 44 | var next = String.Empty; |
| 66 | 45 | ||
| 67 | var command = Commands.Unknown; | 46 | var command = Commands.Unknown; |
| @@ -89,99 +68,99 @@ namespace WixToolset.Core.CommandLine | |||
| 89 | var outputsFile = String.Empty; | 68 | var outputsFile = String.Empty; |
| 90 | var builtOutputsFile = String.Empty; | 69 | var builtOutputsFile = String.Empty; |
| 91 | 70 | ||
| 92 | this.Parse(context, args, (cmdline, arg) => Enum.TryParse(arg, true, out command), (cmdline, arg) => | 71 | this.Parse(context, (cmdline, arg) => Enum.TryParse(arg, true, out command), (cmdline, parser, arg) => |
| 93 | { | 72 | { |
| 94 | if (cmdline.IsSwitch(arg)) | 73 | if (parser.IsSwitch(arg)) |
| 95 | { | 74 | { |
| 96 | var parameter = arg.Substring(1); | 75 | var parameter = arg.Substring(1); |
| 97 | switch (parameter.ToLowerInvariant()) | 76 | switch (parameter.ToLowerInvariant()) |
| 98 | { | 77 | { |
| 99 | case "?": | 78 | case "?": |
| 100 | case "h": | 79 | case "h": |
| 101 | case "help": | 80 | case "help": |
| 102 | cmdline.ShowHelp = true; | 81 | cmdline.ShowHelp = true; |
| 103 | return true; | 82 | return true; |
| 104 | 83 | ||
| 105 | case "bindfiles": | 84 | case "bindfiles": |
| 106 | bindFiles = true; | 85 | bindFiles = true; |
| 107 | return true; | 86 | return true; |
| 108 | 87 | ||
| 109 | case "bindpath": | 88 | case "bindpath": |
| 110 | cmdline.GetNextArgumentOrError(bindPaths); | 89 | parser.GetNextArgumentOrError(arg, bindPaths); |
| 111 | return true; | 90 | return true; |
| 112 | 91 | ||
| 113 | case "cc": | 92 | case "cc": |
| 114 | cmdline.GetNextArgumentOrError(ref cabCachePath); | 93 | cabCachePath = parser.GetNextArgumentOrError(arg); |
| 115 | return true; | 94 | return true; |
| 116 | 95 | ||
| 117 | case "culture": | 96 | case "culture": |
| 118 | cmdline.GetNextArgumentOrError(cultures); | 97 | parser.GetNextArgumentOrError(arg, cultures); |
| 119 | return true; | 98 | return true; |
| 120 | case "contentsfile": | 99 | case "contentsfile": |
| 121 | cmdline.GetNextArgumentOrError(ref contentsFile); | 100 | contentsFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 122 | return true; | 101 | return true; |
| 123 | case "outputsfile": | 102 | case "outputsfile": |
| 124 | cmdline.GetNextArgumentOrError(ref outputsFile); | 103 | outputsFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 125 | return true; | 104 | return true; |
| 126 | case "builtoutputsfile": | 105 | case "builtoutputsfile": |
| 127 | cmdline.GetNextArgumentOrError(ref builtOutputsFile); | 106 | builtOutputsFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 128 | return true; | 107 | return true; |
| 129 | 108 | ||
| 130 | case "d": | 109 | case "d": |
| 131 | case "define": | 110 | case "define": |
| 132 | cmdline.GetNextArgumentOrError(defines); | 111 | parser.GetNextArgumentOrError(arg, defines); |
| 133 | return true; | 112 | return true; |
| 134 | 113 | ||
| 135 | case "i": | 114 | case "i": |
| 136 | case "includepath": | 115 | case "includepath": |
| 137 | cmdline.GetNextArgumentOrError(includePaths); | 116 | parser.GetNextArgumentOrError(arg, includePaths); |
| 138 | return true; | 117 | return true; |
| 139 | 118 | ||
| 140 | case "intermediatefolder": | 119 | case "intermediatefolder": |
| 141 | cmdline.GetNextArgumentOrError(ref intermediateFolder); | 120 | intermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg); |
| 142 | return true; | 121 | return true; |
| 143 | 122 | ||
| 144 | case "loc": | 123 | case "loc": |
| 145 | cmdline.GetNextArgumentAsFilePathOrError(locFiles, "localization files"); | 124 | parser.GetNextArgumentAsFilePathOrError(arg, "localization files", locFiles); |
| 146 | return true; | 125 | return true; |
| 147 | 126 | ||
| 148 | case "lib": | 127 | case "lib": |
| 149 | cmdline.GetNextArgumentAsFilePathOrError(libraryFiles, "library files"); | 128 | parser.GetNextArgumentAsFilePathOrError(arg, "library files", libraryFiles); |
| 150 | return true; | 129 | return true; |
| 151 | 130 | ||
| 152 | case "o": | 131 | case "o": |
| 153 | case "out": | 132 | case "out": |
| 154 | cmdline.GetNextArgumentOrError(ref outputFile); | 133 | outputFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 155 | return true; | 134 | return true; |
| 156 | 135 | ||
| 157 | case "outputtype": | 136 | case "outputtype": |
| 158 | cmdline.GetNextArgumentOrError(ref outputType); | 137 | outputType= parser.GetNextArgumentOrError(arg); |
| 159 | return true; | 138 | return true; |
| 160 | 139 | ||
| 161 | case "nologo": | 140 | case "nologo": |
| 162 | showLogo = false; | 141 | showLogo = false; |
| 163 | return true; | 142 | return true; |
| 164 | 143 | ||
| 165 | case "v": | 144 | case "v": |
| 166 | case "verbose": | 145 | case "verbose": |
| 167 | verbose = true; | 146 | verbose = true; |
| 168 | return true; | 147 | return true; |
| 169 | 148 | ||
| 170 | case "version": | 149 | case "version": |
| 171 | case "-version": | 150 | case "-version": |
| 172 | showVersion = true; | 151 | showVersion = true; |
| 173 | return true; | 152 | return true; |
| 174 | 153 | ||
| 175 | case "sval": | 154 | case "sval": |
| 176 | // todo: implement | 155 | // todo: implement |
| 177 | return true; | 156 | return true; |
| 178 | } | 157 | } |
| 179 | 158 | ||
| 180 | return false; | 159 | return false; |
| 181 | } | 160 | } |
| 182 | else | 161 | else |
| 183 | { | 162 | { |
| 184 | files.AddRange(CommandLineHelper.GetFiles(arg, "source code")); | 163 | parser.GetArgumentAsFilePathOrError(arg, "source code", files); |
| 185 | return true; | 164 | return true; |
| 186 | } | 165 | } |
| 187 | }); | 166 | }); |
| @@ -205,22 +184,22 @@ namespace WixToolset.Core.CommandLine | |||
| 205 | 184 | ||
| 206 | switch (command) | 185 | switch (command) |
| 207 | { | 186 | { |
| 208 | case Commands.Build: | 187 | case Commands.Build: |
| 209 | { | 188 | { |
| 210 | var sourceFiles = GatherSourceFiles(files, outputFolder); | 189 | var sourceFiles = GatherSourceFiles(files, outputFolder); |
| 211 | var variables = this.GatherPreprocessorVariables(defines); | 190 | var variables = this.GatherPreprocessorVariables(defines); |
| 212 | var bindPathList = this.GatherBindPaths(bindPaths); | 191 | var bindPathList = this.GatherBindPaths(bindPaths); |
| 213 | var filterCultures = CalculateFilterCultures(cultures); | 192 | var filterCultures = CalculateFilterCultures(cultures); |
| 214 | var type = CalculateOutputType(outputType, outputFile); | 193 | var type = CalculateOutputType(outputType, outputFile); |
| 215 | return new BuildCommand(this.ServiceProvider, sourceFiles, variables, locFiles, libraryFiles, filterCultures, outputFile, type, cabCachePath, bindFiles, bindPathList, includePaths, intermediateFolder, contentsFile, outputsFile, builtOutputsFile); | 194 | return new BuildCommand(this.ServiceProvider, sourceFiles, variables, locFiles, libraryFiles, filterCultures, outputFile, type, cabCachePath, bindFiles, bindPathList, includePaths, intermediateFolder, contentsFile, outputsFile, builtOutputsFile); |
| 216 | } | 195 | } |
| 217 | 196 | ||
| 218 | case Commands.Compile: | 197 | case Commands.Compile: |
| 219 | { | 198 | { |
| 220 | var sourceFiles = GatherSourceFiles(files, outputFolder); | 199 | var sourceFiles = GatherSourceFiles(files, outputFolder); |
| 221 | var variables = GatherPreprocessorVariables(defines); | 200 | var variables = GatherPreprocessorVariables(defines); |
| 222 | return new CompileCommand(this.ServiceProvider, sourceFiles, variables); | 201 | return new CompileCommand(this.ServiceProvider, sourceFiles, variables); |
| 223 | } | 202 | } |
| 224 | } | 203 | } |
| 225 | 204 | ||
| 226 | return null; | 205 | return null; |
| @@ -262,63 +241,87 @@ namespace WixToolset.Core.CommandLine | |||
| 262 | 241 | ||
| 263 | switch (outputType.ToLowerInvariant()) | 242 | switch (outputType.ToLowerInvariant()) |
| 264 | { | 243 | { |
| 265 | case "bundle": | 244 | case "bundle": |
| 266 | case ".exe": | 245 | case ".exe": |
| 267 | return OutputType.Bundle; | 246 | return OutputType.Bundle; |
| 268 | 247 | ||
| 269 | case "library": | 248 | case "library": |
| 270 | case ".wixlib": | 249 | case ".wixlib": |
| 271 | return OutputType.Library; | 250 | return OutputType.Library; |
| 272 | 251 | ||
| 273 | case "module": | 252 | case "module": |
| 274 | case ".msm": | 253 | case ".msm": |
| 275 | return OutputType.Module; | 254 | return OutputType.Module; |
| 276 | 255 | ||
| 277 | case "patch": | 256 | case "patch": |
| 278 | case ".msp": | 257 | case ".msp": |
| 279 | return OutputType.Patch; | 258 | return OutputType.Patch; |
| 280 | 259 | ||
| 281 | case ".pcp": | 260 | case ".pcp": |
| 282 | return OutputType.PatchCreation; | 261 | return OutputType.PatchCreation; |
| 283 | 262 | ||
| 284 | case "product": | 263 | case "product": |
| 285 | case "package": | 264 | case "package": |
| 286 | case ".msi": | 265 | case ".msi": |
| 287 | return OutputType.Product; | 266 | return OutputType.Product; |
| 288 | 267 | ||
| 289 | case "transform": | 268 | case "transform": |
| 290 | case ".mst": | 269 | case ".mst": |
| 291 | return OutputType.Transform; | 270 | return OutputType.Transform; |
| 292 | 271 | ||
| 293 | case "intermediatepostlink": | 272 | case "intermediatepostlink": |
| 294 | case ".wixipl": | 273 | case ".wixipl": |
| 295 | return OutputType.IntermediatePostLink; | 274 | return OutputType.IntermediatePostLink; |
| 296 | } | 275 | } |
| 297 | 276 | ||
| 298 | return OutputType.Unknown; | 277 | return OutputType.Unknown; |
| 299 | } | 278 | } |
| 300 | 279 | ||
| 301 | #if UNUSED | 280 | private ICommandLine Parse(ICommandLineContext context, Func<CommandLineParser, string, bool> parseCommand, Func<CommandLineParser, IParseCommandLine, string, bool> parseArgument) |
| 302 | private static CommandLine Parse(string commandLineString, Func<CommandLine, string, bool> parseArgument) | ||
| 303 | { | 281 | { |
| 304 | var arguments = CommandLine.ParseArgumentsToArray(commandLineString).ToArray(); | 282 | var extensions = this.ExtensionManager.Create<IExtensionCommandLine>(); |
| 305 | |||
| 306 | return CommandLine.Parse(arguments, null, parseArgument); | ||
| 307 | } | ||
| 308 | 283 | ||
| 309 | private static CommandLine Parse(string[] commandLineArguments, Func<CommandLine, string, bool> parseArgument) | 284 | foreach (var extension in extensions) |
| 310 | { | 285 | { |
| 311 | return CommandLine.Parse(commandLineArguments, null, parseArgument); | 286 | extension.PreParse(context); |
| 312 | } | 287 | } |
| 313 | #endif | ||
| 314 | 288 | ||
| 315 | private ICommandLine Parse(ICommandLineContext context, string[] commandLineArguments, Func<CommandLineParser, string, bool> parseCommand, Func<CommandLineParser, string, bool> parseArgument) | 289 | var parser = context.Arguments.Parse(); |
| 316 | { | ||
| 317 | this.FlattenArgumentsWithResponseFilesIntoOriginalArguments(commandLineArguments); | ||
| 318 | 290 | ||
| 319 | this.QueueArgumentsAndLoadExtensions(this.OriginalArguments); | 291 | while (!this.ShowHelp && |
| 292 | String.IsNullOrEmpty(parser.ErrorArgument) && | ||
| 293 | parser.TryGetNextSwitchOrArgument(out var arg)) | ||
| 294 | { | ||
| 295 | if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments. | ||
| 296 | { | ||
| 297 | continue; | ||
| 298 | } | ||
| 320 | 299 | ||
| 321 | this.ProcessRemainingArguments(context, parseArgument, parseCommand); | 300 | if (parser.IsSwitch(arg)) |
| 301 | { | ||
| 302 | if (!parseArgument(this, parser, arg) && | ||
| 303 | !this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions)) | ||
| 304 | { | ||
| 305 | parser.ErrorArgument = arg; | ||
| 306 | } | ||
| 307 | } | ||
| 308 | else if (String.IsNullOrEmpty(this.ActiveCommand) && parseCommand != null) // First non-switch must be the command, if commands are supported. | ||
| 309 | { | ||
| 310 | if (parseCommand(this, arg)) | ||
| 311 | { | ||
| 312 | this.ActiveCommand = arg; | ||
| 313 | } | ||
| 314 | else | ||
| 315 | { | ||
| 316 | parser.ErrorArgument = arg; | ||
| 317 | } | ||
| 318 | } | ||
| 319 | else if (!this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions) && | ||
| 320 | !parseArgument(this, parser, arg)) | ||
| 321 | { | ||
| 322 | parser.ErrorArgument = arg; | ||
| 323 | } | ||
| 324 | } | ||
| 322 | 325 | ||
| 323 | return this; | 326 | return this; |
| 324 | } | 327 | } |
| @@ -358,7 +361,7 @@ namespace WixToolset.Core.CommandLine | |||
| 358 | return variables; | 361 | return variables; |
| 359 | } | 362 | } |
| 360 | 363 | ||
| 361 | private IEnumerable<BindPath> GatherBindPaths(IEnumerable<string> bindPaths) | 364 | private IEnumerable<BindPath> GatherBindPaths(IEnumerable<string> bindPaths) |
| 362 | { | 365 | { |
| 363 | var result = new List<BindPath>(); | 366 | var result = new List<BindPath>(); |
| 364 | 367 | ||
| @@ -379,172 +382,11 @@ namespace WixToolset.Core.CommandLine | |||
| 379 | return result; | 382 | return result; |
| 380 | } | 383 | } |
| 381 | 384 | ||
| 382 | /// <summary> | 385 | private bool TryParseCommandLineArgumentWithExtension(string arg, IParseCommandLine parse, IEnumerable<IExtensionCommandLine> extensions) |
| 383 | /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity | ||
| 384 | /// </summary> | ||
| 385 | /// <param name="args">The list of strings to check.</param> | ||
| 386 | /// <param name="index">The index (in args) of the commandline parameter to be validated.</param> | ||
| 387 | /// <returns>True if a valid switch exists there, false if not.</returns> | ||
| 388 | public bool IsSwitch(string arg) | ||
| 389 | { | ||
| 390 | return arg != null && arg.Length > 1 && ('/' == arg[0] || '-' == arg[0]); | ||
| 391 | } | ||
| 392 | |||
| 393 | /// <summary> | ||
| 394 | /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity | ||
| 395 | /// </summary> | ||
| 396 | /// <param name="args">The list of strings to check.</param> | ||
| 397 | /// <param name="index">The index (in args) of the commandline parameter to be validated.</param> | ||
| 398 | /// <returns>True if a valid switch exists there, false if not.</returns> | ||
| 399 | public bool IsSwitchAt(IEnumerable<string> args, int index) | ||
| 400 | { | ||
| 401 | var arg = args.ElementAtOrDefault(index); | ||
| 402 | return IsSwitch(arg); | ||
| 403 | } | ||
| 404 | |||
| 405 | public void GetNextArgumentOrError(ref string arg) | ||
| 406 | { | ||
| 407 | this.TryGetNextArgumentOrError(out arg); | ||
| 408 | } | ||
| 409 | |||
| 410 | public void GetNextArgumentOrError(IList<string> args) | ||
| 411 | { | ||
| 412 | if (this.TryGetNextArgumentOrError(out var arg)) | ||
| 413 | { | ||
| 414 | args.Add(arg); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | public void GetNextArgumentAsFilePathOrError(IList<string> args, string fileType) | ||
| 419 | { | ||
| 420 | if (this.TryGetNextArgumentOrError(out var arg)) | ||
| 421 | { | ||
| 422 | foreach (var path in CommandLineHelper.GetFiles(arg, fileType)) | ||
| 423 | { | ||
| 424 | args.Add(path); | ||
| 425 | } | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | public bool TryGetNextArgumentOrError(out string arg) | ||
| 430 | { | ||
| 431 | if (TryDequeue(this.RemainingArguments, out arg) && !this.IsSwitch(arg)) | ||
| 432 | { | ||
| 433 | return true; | ||
| 434 | } | ||
| 435 | |||
| 436 | this.ErrorArgument = arg ?? CommandLineParser.ExpectedArgument; | ||
| 437 | |||
| 438 | return false; | ||
| 439 | } | ||
| 440 | |||
| 441 | private static bool TryDequeue(Queue<string> q, out string arg) | ||
| 442 | { | ||
| 443 | if (q.Count > 0) | ||
| 444 | { | ||
| 445 | arg = q.Dequeue(); | ||
| 446 | return true; | ||
| 447 | } | ||
| 448 | |||
| 449 | arg = null; | ||
| 450 | return false; | ||
| 451 | } | ||
| 452 | |||
| 453 | private void FlattenArgumentsWithResponseFilesIntoOriginalArguments(string[] commandLineArguments) | ||
| 454 | { | ||
| 455 | List<string> args = new List<string>(); | ||
| 456 | |||
| 457 | foreach (var arg in commandLineArguments) | ||
| 458 | { | ||
| 459 | if ('@' == arg[0]) | ||
| 460 | { | ||
| 461 | var responseFileArguments = CommandLineParser.ParseResponseFile(arg.Substring(1)); | ||
| 462 | args.AddRange(responseFileArguments); | ||
| 463 | } | ||
| 464 | else | ||
| 465 | { | ||
| 466 | args.Add(arg); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | this.OriginalArguments = args.ToArray(); | ||
| 471 | } | ||
| 472 | |||
| 473 | private void QueueArgumentsAndLoadExtensions(string[] args) | ||
| 474 | { | 386 | { |
| 475 | for (var i = 0; i < args.Length; ++i) | ||
| 476 | { | ||
| 477 | var arg = args[i]; | ||
| 478 | |||
| 479 | if ("-ext" == arg || "/ext" == arg) | ||
| 480 | { | ||
| 481 | if (!this.IsSwitchAt(args, ++i)) | ||
| 482 | { | ||
| 483 | this.ExtensionManager.Load(args[i]); | ||
| 484 | } | ||
| 485 | else | ||
| 486 | { | ||
| 487 | this.ErrorArgument = arg; | ||
| 488 | break; | ||
| 489 | } | ||
| 490 | } | ||
| 491 | else | ||
| 492 | { | ||
| 493 | this.RemainingArguments.Enqueue(arg); | ||
| 494 | } | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | private void ProcessRemainingArguments(ICommandLineContext context, Func<CommandLineParser, string, bool> parseArgument, Func<CommandLineParser, string, bool> parseCommand) | ||
| 499 | { | ||
| 500 | var extensions = this.ExtensionManager.Create<IExtensionCommandLine>(); | ||
| 501 | |||
| 502 | foreach (var extension in extensions) | 387 | foreach (var extension in extensions) |
| 503 | { | 388 | { |
| 504 | extension.PreParse(context); | 389 | if (extension.TryParseArgument(parse, arg)) |
| 505 | } | ||
| 506 | |||
| 507 | while (!this.ShowHelp && | ||
| 508 | String.IsNullOrEmpty(this.ErrorArgument) && | ||
| 509 | TryDequeue(this.RemainingArguments, out var arg)) | ||
| 510 | { | ||
| 511 | if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments. | ||
| 512 | { | ||
| 513 | continue; | ||
| 514 | } | ||
| 515 | |||
| 516 | if ('-' == arg[0] || '/' == arg[0]) | ||
| 517 | { | ||
| 518 | if (!parseArgument(this, arg) && | ||
| 519 | !this.TryParseCommandLineArgumentWithExtension(arg, extensions)) | ||
| 520 | { | ||
| 521 | this.ErrorArgument = arg; | ||
| 522 | } | ||
| 523 | } | ||
| 524 | else if (String.IsNullOrEmpty(this.ActiveCommand) && parseCommand != null) // First non-switch must be the command, if commands are supported. | ||
| 525 | { | ||
| 526 | if (parseCommand(this, arg)) | ||
| 527 | { | ||
| 528 | this.ActiveCommand = arg; | ||
| 529 | } | ||
| 530 | else | ||
| 531 | { | ||
| 532 | this.ErrorArgument = arg; | ||
| 533 | } | ||
| 534 | } | ||
| 535 | else if (!this.TryParseCommandLineArgumentWithExtension(arg, extensions) && | ||
| 536 | !parseArgument(this, arg)) | ||
| 537 | { | ||
| 538 | this.ErrorArgument = arg; | ||
| 539 | } | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | private bool TryParseCommandLineArgumentWithExtension(string arg, IEnumerable<IExtensionCommandLine> extensions) | ||
| 544 | { | ||
| 545 | foreach (var extension in extensions) | ||
| 546 | { | ||
| 547 | if (extension.TryParseArgument(this, arg)) | ||
| 548 | { | 390 | { |
| 549 | return true; | 391 | return true; |
| 550 | } | 392 | } |
| @@ -552,110 +394,5 @@ namespace WixToolset.Core.CommandLine | |||
| 552 | 394 | ||
| 553 | return false; | 395 | return false; |
| 554 | } | 396 | } |
| 555 | |||
| 556 | private static List<string> ParseResponseFile(string responseFile) | ||
| 557 | { | ||
| 558 | string arguments; | ||
| 559 | |||
| 560 | using (StreamReader reader = new StreamReader(responseFile)) | ||
| 561 | { | ||
| 562 | arguments = reader.ReadToEnd(); | ||
| 563 | } | ||
| 564 | |||
| 565 | return CommandLineParser.ParseArgumentsToArray(arguments); | ||
| 566 | } | ||
| 567 | |||
| 568 | private static List<string> ParseArgumentsToArray(string arguments) | ||
| 569 | { | ||
| 570 | // Scan and parse the arguments string, dividing up the arguments based on whitespace. | ||
| 571 | // Unescaped quotes cause whitespace to be ignored, while the quotes themselves are removed. | ||
| 572 | // Quotes may begin and end inside arguments; they don't necessarily just surround whole arguments. | ||
| 573 | // Escaped quotes and escaped backslashes also need to be unescaped by this process. | ||
| 574 | |||
| 575 | // Collects the final list of arguments to be returned. | ||
| 576 | var argsList = new List<string>(); | ||
| 577 | |||
| 578 | // True if we are inside an unescaped quote, meaning whitespace should be ignored. | ||
| 579 | var insideQuote = false; | ||
| 580 | |||
| 581 | // Index of the start of the current argument substring; either the start of the argument | ||
| 582 | // or the start of a quoted or unquoted sequence within it. | ||
| 583 | var partStart = 0; | ||
| 584 | |||
| 585 | // The current argument string being built; when completed it will be added to the list. | ||
| 586 | var arg = new StringBuilder(); | ||
| 587 | |||
| 588 | for (int i = 0; i <= arguments.Length; i++) | ||
| 589 | { | ||
| 590 | if (i == arguments.Length || (Char.IsWhiteSpace(arguments[i]) && !insideQuote)) | ||
| 591 | { | ||
| 592 | // Reached a whitespace separator or the end of the string. | ||
| 593 | |||
| 594 | // Finish building the current argument. | ||
| 595 | arg.Append(arguments.Substring(partStart, i - partStart)); | ||
| 596 | |||
| 597 | // Skip over the whitespace character. | ||
| 598 | partStart = i + 1; | ||
| 599 | |||
| 600 | // Add the argument to the list if it's not empty. | ||
| 601 | if (arg.Length > 0) | ||
| 602 | { | ||
| 603 | argsList.Add(CommandLineParser.ExpandEnvironmentVariables(arg.ToString())); | ||
| 604 | arg.Length = 0; | ||
| 605 | } | ||
| 606 | } | ||
| 607 | else if (i > partStart && arguments[i - 1] == '\\') | ||
| 608 | { | ||
| 609 | // Check the character following an unprocessed backslash. | ||
| 610 | // Unescape quotes, and backslashes followed by a quote. | ||
| 611 | if (arguments[i] == '"' || (arguments[i] == '\\' && arguments.Length > i + 1 && arguments[i + 1] == '"')) | ||
| 612 | { | ||
| 613 | // Unescape the quote or backslash by skipping the preceeding backslash. | ||
| 614 | arg.Append(arguments.Substring(partStart, i - 1 - partStart)); | ||
| 615 | arg.Append(arguments[i]); | ||
| 616 | partStart = i + 1; | ||
| 617 | } | ||
| 618 | } | ||
| 619 | else if (arguments[i] == '"') | ||
| 620 | { | ||
| 621 | // Add the quoted or unquoted section to the argument string. | ||
| 622 | arg.Append(arguments.Substring(partStart, i - partStart)); | ||
| 623 | |||
| 624 | // And skip over the quote character. | ||
| 625 | partStart = i + 1; | ||
| 626 | |||
| 627 | insideQuote = !insideQuote; | ||
| 628 | } | ||
| 629 | } | ||
| 630 | |||
| 631 | return argsList; | ||
| 632 | } | ||
| 633 | |||
| 634 | private static string ExpandEnvironmentVariables(string arguments) | ||
| 635 | { | ||
| 636 | var id = Environment.GetEnvironmentVariables(); | ||
| 637 | |||
| 638 | var regex = new Regex("(?<=\\%)(?:[\\w\\.]+)(?=\\%)"); | ||
| 639 | MatchCollection matches = regex.Matches(arguments); | ||
| 640 | |||
| 641 | string value = String.Empty; | ||
| 642 | for (int i = 0; i <= (matches.Count - 1); i++) | ||
| 643 | { | ||
| 644 | try | ||
| 645 | { | ||
| 646 | var key = matches[i].Value; | ||
| 647 | regex = new Regex(String.Concat("(?i)(?:\\%)(?:", key, ")(?:\\%)")); | ||
| 648 | value = id[key].ToString(); | ||
| 649 | arguments = regex.Replace(arguments, value); | ||
| 650 | } | ||
| 651 | catch (NullReferenceException) | ||
| 652 | { | ||
| 653 | // Collapse unresolved environment variables. | ||
| 654 | arguments = regex.Replace(arguments, value); | ||
| 655 | } | ||
| 656 | } | ||
| 657 | |||
| 658 | return arguments; | ||
| 659 | } | ||
| 660 | } | 397 | } |
| 661 | } | 398 | } |
