public void ContainsEscapedWildcards() { Assert.False(EscapingUtilities.ContainsEscapedWildcards("NoStarOrQMark")); Assert.False(EscapingUtilities.ContainsEscapedWildcards("%4")); Assert.False(EscapingUtilities.ContainsEscapedWildcards("%3B")); Assert.False(EscapingUtilities.ContainsEscapedWildcards("%2B")); Assert.True(EscapingUtilities.ContainsEscapedWildcards("%2a")); Assert.True(EscapingUtilities.ContainsEscapedWildcards("%2A")); Assert.True(EscapingUtilities.ContainsEscapedWildcards("%3F")); Assert.True(EscapingUtilities.ContainsEscapedWildcards("%3f")); }
void ProcessItemSpec(OperationBuilder operationBuilder, string itemSpec, ElementLocation itemSpecLocation) { // Code corresponds to Evaluator.CreateItemsFromInclude // STEP 1: Expand properties in Include string evaluatedIncludeEscaped = _outerExpander.ExpandIntoStringLeaveEscaped(itemSpec, ExpanderOptions.ExpandProperties, itemSpecLocation); // STEP 2: Split Include on any semicolons, and take each split in turn if (evaluatedIncludeEscaped.Length > 0) { IList <string> includeSplitsEscaped = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedIncludeEscaped); foreach (string includeSplitEscaped in includeSplitsEscaped) { // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string bool isItemListExpression; ProcessSingleItemVectorExpressionForInclude(includeSplitEscaped, operationBuilder, itemSpecLocation, out isItemListExpression); if (!isItemListExpression) { // The expression is not of the form "@(X)". Treat as string // Code corresponds to EngineFileUtilities.GetFileList bool containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(includeSplitEscaped); bool containsRealWildcards = FileMatcher.HasWildcards(includeSplitEscaped); if (containsEscapedWildcards && containsRealWildcards) { // Umm, this makes no sense. The item's Include has both escaped wildcards and // real wildcards. What does he want us to do? Go to the file system and find // files that literally have '*' in their filename? Well, that's not going to // happen because '*' is an illegal character to have in a filename. // Just return the original string. operationBuilder.Operations.Add(Tuple.Create(ItemOperationType.Value, (object)includeSplitEscaped)); } else if (!containsEscapedWildcards && containsRealWildcards) { // Unescape before handing it to the filesystem. string filespecUnescaped = EscapingUtilities.UnescapeAll(includeSplitEscaped); operationBuilder.Operations.Add(Tuple.Create(ItemOperationType.Glob, (object)filespecUnescaped)); } else { // No real wildcards means we just return the original string. Don't even bother // escaping ... it should already be escaped appropriately since it came directly // from the project file operationBuilder.Operations.Add(Tuple.Create(ItemOperationType.Value, (object)includeSplitEscaped)); } } } } }
internal static bool FilespecHasWildcards(string filespecEscaped) { if (!FileMatcher.HasWildcards(filespecEscaped)) { return(false); } // If the item's Include has both escaped wildcards and real wildcards, then it's // not clear what they are asking us to do. Go to the file system and find // files that literally have '*' in their filename? Well, that's not going to // happen because '*' is an illegal character to have in a filename. return(!EscapingUtilities.ContainsEscapedWildcards(filespecEscaped)); }
private static bool FilespecHasWildcards(string filespecEscaped) { bool containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(filespecEscaped); bool containsRealWildcards = FileMatcher.HasWildcards(filespecEscaped); if (containsEscapedWildcards && containsRealWildcards) { // Umm, this makes no sense. The item's Include has both escaped wildcards and // real wildcards. What does he want us to do? Go to the file system and find // files that literally have '*' in their filename? Well, that's not going to // happen because '*' is an illegal character to have in a filename. return(false); } else if (!containsEscapedWildcards && containsRealWildcards) { return(true); } else { return(false); } }
private static bool IsValidExclude(string exclude) { // TODO: assumption on legal path characters: https://github.com/dotnet/msbuild/issues/781 // Excludes that have both wildcards and non escaped wildcards will never be matched on Windows, because // wildcard characters are invalid in Windows paths. // Filtering these excludes early keeps the glob expander simpler. Otherwise unescaping logic would reach all the way down to // filespec parsing (parse escaped string (to correctly ignore escaped wildcards) and then // unescape the path fragments to unfold potentially escaped wildcard chars) var hasBothWildcardsAndEscapedWildcards = FileMatcher.HasWildcards(exclude) && EscapingUtilities.ContainsEscapedWildcards(exclude); return(!hasBothWildcardsAndEscapedWildcards); }
private IEnumerable <ItemFragment> BuildItemFragments(IElementLocation itemSpecLocation, bool expandProperties) { var builder = ImmutableList.CreateBuilder <ItemFragment>(); // Code corresponds to Evaluator.CreateItemsFromInclude var evaluatedItemspecEscaped = ItemSpecString; if (string.IsNullOrEmpty(evaluatedItemspecEscaped)) { return(builder.ToImmutable()); } // STEP 1: Expand properties in Include if (expandProperties) { evaluatedItemspecEscaped = Expander.ExpandIntoStringLeaveEscaped(ItemSpecString, ExpanderOptions.ExpandProperties, itemSpecLocation); } // STEP 2: Split Include on any semicolons, and take each split in turn if (evaluatedItemspecEscaped.Length > 0) { var splitsEscaped = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedItemspecEscaped); foreach (var splitEscaped in splitsEscaped) { // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string bool isItemListExpression; var itemReferenceFragment = ProcessItemExpression(splitEscaped, itemSpecLocation, out isItemListExpression); if (isItemListExpression) { builder.Add(itemReferenceFragment); } else { // The expression is not of the form "@(X)". Treat as string // Code corresponds to EngineFileUtilities.GetFileList var containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(splitEscaped); var containsRealWildcards = FileMatcher.HasWildcards(splitEscaped); // '*' is an illegal character to have in a filename. // todo file-system assumption on legal path characters: https://github.com/Microsoft/msbuild/issues/781 if (containsEscapedWildcards && containsRealWildcards) { // Just return the original string. builder.Add(new ValueFragment(splitEscaped, itemSpecLocation.File)); } else if (!containsEscapedWildcards && containsRealWildcards) { // Unescape before handing it to the filesystem. var filespecUnescaped = EscapingUtilities.UnescapeAll(splitEscaped); builder.Add(new GlobFragment(filespecUnescaped, itemSpecLocation.File)); } else { // No real wildcards means we just return the original string. Don't even bother // escaping ... it should already be escaped appropriately since it came directly // from the project file builder.Add(new ValueFragment(splitEscaped, itemSpecLocation.File)); } } } } return(builder.ToImmutable()); }
internal static bool ContainsEscapedWildcards(this string escapedString) { return(EscapingUtilities.ContainsEscapedWildcards(escapedString)); }
/// <summary> /// Used for the purposes of evaluating an item specification. Given a filespec that may include wildcard characters * and /// ?, we translate it into an actual list of files. If the input filespec doesn't contain any wildcard characters, and it /// doesn't appear to point to an actual file on disk, then we just give back the input string as an array of length one, /// assuming that it wasn't really intended to be a filename (as items are not required to necessarily represent files). /// </summary> /// <owner>RGoel</owner> /// <param name="filespec">The filespec to evaluate.</param> /// <returns>Array of file paths.</returns> internal static string[] GetFileListEscaped ( string directory, string filespec ) { ErrorUtilities.VerifyThrow(filespec.Length > 0, "Need a valid file-spec."); string[] fileList; bool containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(filespec); bool containsRealWildcards = FileMatcher.HasWildcards(filespec); if (containsEscapedWildcards && containsRealWildcards) { // Umm, this makes no sense. The item's Include has both escaped wildcards and // real wildcards. What does he want us to do? Go to the file system and find // files that literally have '*' in their filename? Well, that's not going to // happen because '*' is an illegal character to have in a filename. // Just return the original string. fileList = new string[] { EscapingUtilities.Escape(filespec) }; } else if (!containsEscapedWildcards && containsRealWildcards) { // Unescape before handing it to the filesystem. string filespecUnescaped = EscapingUtilities.UnescapeAll(filespec); // Get the list of actual files which match the filespec. Put // the list into a string array. If the filespec started out // as a relative path, we will get back a bunch of relative paths. // If the filespec started out as an absolute path, we will get // back a bunch of absolute paths. fileList = FileMatcher.GetFiles(directory, filespecUnescaped); ErrorUtilities.VerifyThrow(fileList != null, "We must have a list of files here, even if it's empty."); // Before actually returning the file list, we sort them alphabetically. This // provides a certain amount of extra determinism and reproducability. That is, // we're sure that the build will behave in exactly the same way every time, // and on every machine. Array.Sort(fileList); // We must now go back and make sure all special characters are escaped because we always // store data in the engine in escaped form so it doesn't screw up our parsing. // Note that this means that characters that were not escaped in the original filespec // may now be escaped, but that's not easy to avoid. for (int i = 0; i < fileList.Length; i++) { fileList[i] = EscapingUtilities.Escape(fileList[i]); } } else { // No real wildcards means we just return the original string. Don't even bother // escaping ... it should already be escaped appropriately since it came directly // from the project file or the OM host. fileList = new string[] { filespec }; } return(fileList); }
private List <ItemSpecFragment> BuildItemFragments(IElementLocation itemSpecLocation, string projectDirectory, bool expandProperties) { // Code corresponds to Evaluator.CreateItemsFromInclude var evaluatedItemspecEscaped = ItemSpecString; if (string.IsNullOrEmpty(evaluatedItemspecEscaped)) { return(new List <ItemSpecFragment>()); } // STEP 1: Expand properties in Include if (expandProperties) { evaluatedItemspecEscaped = Expander.ExpandIntoStringLeaveEscaped( ItemSpecString, ExpanderOptions.ExpandProperties, itemSpecLocation); } var semicolonCount = 0; foreach (var c in evaluatedItemspecEscaped) { if (c == ';') { semicolonCount++; } } // estimate the number of fragments with the number of semicolons. This is will overestimate in case of transforms with semicolons, but won't underestimate. var fragments = new List <ItemSpecFragment>(semicolonCount + 1); // STEP 2: Split Include on any semicolons, and take each split in turn if (evaluatedItemspecEscaped.Length > 0) { var splitsEscaped = ExpressionShredder.SplitSemiColonSeparatedList(evaluatedItemspecEscaped); foreach (var splitEscaped in splitsEscaped) { // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string var itemReferenceFragment = ProcessItemExpression( splitEscaped, itemSpecLocation, projectDirectory, out var isItemListExpression); if (isItemListExpression) { fragments.Add(itemReferenceFragment); } else { // The expression is not of the form "@(X)". Treat as string // Code corresponds to EngineFileUtilities.GetFileList if (!FileMatcher.HasWildcards(splitEscaped)) { // No real wildcards means we just return the original string. Don't even bother // escaping ... it should already be escaped appropriately since it came directly // from the project file fragments.Add(new ValueFragment(splitEscaped, projectDirectory)); } else if (EscapingUtilities.ContainsEscapedWildcards(splitEscaped)) { // '*' is an illegal character to have in a filename. // todo: file-system assumption on legal path characters: https://github.com/dotnet/msbuild/issues/781 // Just return the original string. fragments.Add(new ValueFragment(splitEscaped, projectDirectory)); } else { // Unescape before handing it to the filesystem. var filespecUnescaped = EscapingUtilities.UnescapeAll(splitEscaped); fragments.Add(new GlobFragment(filespecUnescaped, projectDirectory)); } } } } return(fragments); }