diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs | 11 | ||||
-rw-r--r-- | src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs | 73 | ||||
-rw-r--r-- | src/WixToolset.Core/Common.cs | 271 | ||||
-rw-r--r-- | src/WixToolset.Core/Compiler.cs | 47 | ||||
-rw-r--r-- | src/WixToolset.Core/CompilerCore.cs | 55 | ||||
-rw-r--r-- | src/WixToolset.Core/Compiler_2.cs | 28 | ||||
-rw-r--r-- | src/WixToolset.Core/Compiler_Bundle.cs | 2 | ||||
-rw-r--r-- | src/WixToolset.Core/Compiler_UI.cs | 2 | ||||
-rw-r--r-- | src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs | 132 | ||||
-rw-r--r-- | src/WixToolset.Core/ParsedWixVariable.cs | 19 | ||||
-rw-r--r-- | src/WixToolset.Core/VariableResolver.cs | 188 |
11 files changed, 454 insertions, 374 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs index d5601fad..36172b5e 100644 --- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs | |||
@@ -226,11 +226,12 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
226 | value = value.Replace("$(", "$$("); | 226 | value = value.Replace("$(", "$$("); |
227 | 227 | ||
228 | // escape things that look like wix variables | 228 | // escape things that look like wix variables |
229 | var matches = Common.WixVariableRegex.Matches(value); | 229 | // TODO: Evaluate this requirement. |
230 | for (var j = matches.Count - 1; 0 <= j; j--) | 230 | //var matches = Common.WixVariableRegex.Matches(value); |
231 | { | 231 | //for (var j = matches.Count - 1; 0 <= j; j--) |
232 | value = value.Insert(matches[j].Index, "!"); | 232 | //{ |
233 | } | 233 | // value = value.Insert(matches[j].Index, "!"); |
234 | //} | ||
234 | 235 | ||
235 | row[i] = value; | 236 | row[i] = value; |
236 | break; | 237 | break; |
diff --git a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs index a10b98dc..ebabed47 100644 --- a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs +++ b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs | |||
@@ -69,11 +69,11 @@ namespace WixToolset.Core.Bind | |||
69 | } | 69 | } |
70 | 70 | ||
71 | // add specialization for ProductVersion fields | 71 | // add specialization for ProductVersion fields |
72 | string keyProductVersion = "property.ProductVersion"; | 72 | var keyProductVersion = "property.ProductVersion"; |
73 | if (this.VariableCache.TryGetValue(keyProductVersion, out var versionValue) && Version.TryParse(versionValue, out Version productVersion)) | 73 | if (this.VariableCache.TryGetValue(keyProductVersion, out var versionValue) && Version.TryParse(versionValue, out Version productVersion)) |
74 | { | 74 | { |
75 | // Don't add the variable if it already exists (developer defined a property with the same name). | 75 | // Don't add the variable if it already exists (developer defined a property with the same name). |
76 | string fieldKey = String.Concat(keyProductVersion, ".Major"); | 76 | var fieldKey = String.Concat(keyProductVersion, ".Major"); |
77 | if (!this.VariableCache.ContainsKey(fieldKey)) | 77 | if (!this.VariableCache.ContainsKey(fieldKey)) |
78 | { | 78 | { |
79 | this.VariableCache[fieldKey] = productVersion.Major.ToString(CultureInfo.InvariantCulture); | 79 | this.VariableCache[fieldKey] = productVersion.Major.ToString(CultureInfo.InvariantCulture); |
@@ -115,68 +115,45 @@ namespace WixToolset.Core.Bind | |||
115 | 115 | ||
116 | private static string ResolveDelayedVariables(SourceLineNumber sourceLineNumbers, string value, IDictionary<string, string> resolutionData) | 116 | private static string ResolveDelayedVariables(SourceLineNumber sourceLineNumbers, string value, IDictionary<string, string> resolutionData) |
117 | { | 117 | { |
118 | var matches = Common.WixVariableRegex.Matches(value); | 118 | var start = 0; |
119 | 119 | ||
120 | if (0 < matches.Count) | 120 | while (Common.TryParseWixVariable(value, start, out var parsed)) |
121 | { | 121 | { |
122 | var sb = new StringBuilder(value); | 122 | if (parsed.Namespace == "bind") |
123 | |||
124 | // notice how this code walks backward through the list | ||
125 | // because it modifies the string as we go through it | ||
126 | for (int i = matches.Count - 1; 0 <= i; i--) | ||
127 | { | 123 | { |
128 | string variableNamespace = matches[i].Groups["namespace"].Value; | 124 | var key = String.Concat(parsed.Name, ".", parsed.Scope); |
129 | string variableId = matches[i].Groups["fullname"].Value; | ||
130 | string variableDefaultValue = null; | ||
131 | string variableScope = null; | ||
132 | 125 | ||
133 | // get the default value if one was specified | 126 | if (!resolutionData.TryGetValue(key, out var resolvedValue)) |
134 | if (matches[i].Groups["value"].Success) | ||
135 | { | 127 | { |
136 | variableDefaultValue = matches[i].Groups["value"].Value; | 128 | resolvedValue = parsed.DefaultValue; |
137 | } | 129 | } |
138 | 130 | ||
139 | // get the scope if one was specified | 131 | // insert the resolved value if it was found or display an error |
140 | if (matches[i].Groups["scope"].Success) | 132 | if (null != resolvedValue) |
141 | { | 133 | { |
142 | variableScope = matches[i].Groups["scope"].Value; | 134 | if (parsed.Index == 0 && parsed.Length == value.Length) |
143 | if ("bind" == variableNamespace) | ||
144 | { | 135 | { |
145 | variableId = matches[i].Groups["name"].Value; | 136 | value = resolvedValue; |
137 | } | ||
138 | else | ||
139 | { | ||
140 | var sb = new StringBuilder(value); | ||
141 | sb.Remove(parsed.Index, parsed.Length); | ||
142 | sb.Insert(parsed.Index, resolvedValue); | ||
143 | value = sb.ToString(); | ||
146 | } | 144 | } |
147 | } | ||
148 | 145 | ||
149 | // check for an escape sequence of !! indicating the match is not a variable expression | 146 | start = parsed.Index; |
150 | if (0 < matches[i].Index && '!' == sb[matches[i].Index - 1]) | ||
151 | { | ||
152 | sb.Remove(matches[i].Index - 1, 1); | ||
153 | } | 147 | } |
154 | else | 148 | else |
155 | { | 149 | { |
156 | var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", variableId, variableScope).ToLower(CultureInfo.InvariantCulture); | 150 | throw new WixException(ErrorMessages.UnresolvedBindReference(sourceLineNumbers, value)); |
157 | |||
158 | if (!resolutionData.TryGetValue(key, out var resolvedValue)) | ||
159 | { | ||
160 | resolvedValue = variableDefaultValue; | ||
161 | } | ||
162 | |||
163 | if ("bind" == variableNamespace) | ||
164 | { | ||
165 | // insert the resolved value if it was found or display an error | ||
166 | if (null != resolvedValue) | ||
167 | { | ||
168 | sb.Remove(matches[i].Index, matches[i].Length); | ||
169 | sb.Insert(matches[i].Index, resolvedValue); | ||
170 | } | ||
171 | else | ||
172 | { | ||
173 | throw new WixException(ErrorMessages.UnresolvedBindReference(sourceLineNumbers, value)); | ||
174 | } | ||
175 | } | ||
176 | } | 151 | } |
177 | } | 152 | } |
178 | 153 | else | |
179 | value = sb.ToString(); | 154 | { |
155 | start = parsed.Index + parsed.Length; | ||
156 | } | ||
180 | } | 157 | } |
181 | 158 | ||
182 | return value; | 159 | return value; |
diff --git a/src/WixToolset.Core/Common.cs b/src/WixToolset.Core/Common.cs index a9fc9538..19c77368 100644 --- a/src/WixToolset.Core/Common.cs +++ b/src/WixToolset.Core/Common.cs | |||
@@ -9,7 +9,6 @@ namespace WixToolset.Core | |||
9 | using System.Linq; | 9 | using System.Linq; |
10 | using System.Security.Cryptography; | 10 | using System.Security.Cryptography; |
11 | using System.Text; | 11 | using System.Text; |
12 | using System.Text.RegularExpressions; | ||
13 | using System.Xml; | 12 | using System.Xml; |
14 | using System.Xml.Linq; | 13 | using System.Xml.Linq; |
15 | using WixToolset.Data; | 14 | using WixToolset.Data; |
@@ -102,18 +101,14 @@ namespace WixToolset.Core | |||
102 | // TODO: Find a place to put this that it doesn't have to be public and exposed by WixToolset.Core.dll | 101 | // TODO: Find a place to put this that it doesn't have to be public and exposed by WixToolset.Core.dll |
103 | public static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", "FileAllRights", "ReadAttributes", "WriteAttributes" }; | 102 | public static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", "FileAllRights", "ReadAttributes", "WriteAttributes" }; |
104 | 103 | ||
105 | public static readonly Regex WixVariableRegex = new Regex(@"(\!|\$)\((?<namespace>loc|wix|bind|bindpath)\.(?<fullname>(?<name>[_A-Za-z][0-9A-Za-z_]+)(\.(?<scope>[_A-Za-z][0-9A-Za-z_\.]*))?)(\=(?<value>.+?))?\)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture); | 104 | internal static readonly char[] IllegalLongFilenameCharacters = new[] { '\\', '/', '?', '*', '|', '>', '<', ':', '\"' }; // illegal: \ / ? | > < : / * " |
105 | internal static readonly char[] IllegalRelativeLongFilenameCharacters = new[] { '?', '*', '|', '>', '<', ':', '\"' }; // like illegal, but we allow '\' and '/' | ||
106 | internal static readonly char[] IllegalWildcardLongFilenameCharacters = new[] { '\\', '/', '|', '>', '<', ':', '\"' }; // like illegal: but we allow '*' and '?' | ||
106 | 107 | ||
107 | private static readonly Regex PropertySearch = new Regex(@"\[[#$!]?[a-zA-Z_][a-zA-Z0-9_\.]*]", RegexOptions.Singleline); | 108 | private static readonly char[] IllegalShortFilenameCharacters = new[] { '\\', '?', '|', '>', '<', ':', '/', '*', '\"', '+', ',', ';', '=', '[', ']', '.', ' ' }; |
108 | private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled); | 109 | private static readonly char[] IllegalWildcardShortFilenameCharacters = new[] { '\\', '|', '>', '<', ':', '/', '\"', '+', ',', ';', '=', '[', ']', '.', ' ' }; |
109 | private static readonly Regex LegalIdentifierCharacters = new Regex(@"^[_A-Za-z][0-9A-Za-z_\.]*$", RegexOptions.Compiled); | ||
110 | private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters | ||
111 | 110 | ||
112 | private const string LegalShortFilenameCharacters = @"[^\\\?|><:/\*""\+,;=\[\]\. ]"; // illegal: \ ? | > < : / * " + , ; = [ ] . (space) | ||
113 | private static readonly Regex LegalShortFilename = new Regex(String.Concat("^", LegalShortFilenameCharacters, @"{1,8}(\.", LegalShortFilenameCharacters, "{0,3})?$"), RegexOptions.Compiled); | ||
114 | 111 | ||
115 | private const string LegalWildcardShortFilenameCharacters = @"[^\\|><:/""\+,;=\[\]\. ]"; // illegal: \ | > < : / " + , ; = [ ] . (space) | ||
116 | private static readonly Regex LegalWildcardShortFilename = new Regex(String.Concat("^", LegalWildcardShortFilenameCharacters, @"{1,16}(\.", LegalWildcardShortFilenameCharacters, "{0,6})?$")); | ||
117 | 112 | ||
118 | /// <summary> | 113 | /// <summary> |
119 | /// Gets a valid code page from the given web name or integer value. | 114 | /// Gets a valid code page from the given web name or integer value. |
@@ -197,79 +192,95 @@ namespace WixToolset.Core | |||
197 | 192 | ||
198 | if (allowWildcards) | 193 | if (allowWildcards) |
199 | { | 194 | { |
200 | if (Common.LegalWildcardShortFilename.IsMatch(filename)) | 195 | var expectedDot = filename.IndexOfAny(IllegalWildcardShortFilenameCharacters); |
196 | if (expectedDot == -1) | ||
201 | { | 197 | { |
202 | bool foundPeriod = false; | 198 | } |
203 | int beforePeriod = 0; | 199 | else if (filename[expectedDot] != '.') |
204 | int afterPeriod = 0; | 200 | { |
201 | return false; | ||
202 | } | ||
203 | else if (expectedDot < filename.Length) | ||
204 | { | ||
205 | var extensionInvalids = filename.IndexOfAny(IllegalShortFilenameCharacters, expectedDot + 1); | ||
206 | if (extensionInvalids != -1) | ||
207 | { | ||
208 | return false; | ||
209 | } | ||
210 | } | ||
205 | 211 | ||
206 | // count the number of characters before and after the period | 212 | var foundPeriod = false; |
207 | // '*' is not counted because it may represent zero characters | 213 | var beforePeriod = 0; |
208 | foreach (char character in filename) | 214 | var afterPeriod = 0; |
215 | |||
216 | // count the number of characters before and after the period | ||
217 | // '*' is not counted because it may represent zero characters | ||
218 | foreach (var character in filename) | ||
219 | { | ||
220 | if ('.' == character) | ||
221 | { | ||
222 | foundPeriod = true; | ||
223 | } | ||
224 | else if ('*' != character) | ||
209 | { | 225 | { |
210 | if ('.' == character) | 226 | if (foundPeriod) |
211 | { | 227 | { |
212 | foundPeriod = true; | 228 | afterPeriod++; |
213 | } | 229 | } |
214 | else if ('*' != character) | 230 | else |
215 | { | 231 | { |
216 | if (foundPeriod) | 232 | beforePeriod++; |
217 | { | ||
218 | afterPeriod++; | ||
219 | } | ||
220 | else | ||
221 | { | ||
222 | beforePeriod++; | ||
223 | } | ||
224 | } | 233 | } |
225 | } | 234 | } |
235 | } | ||
226 | 236 | ||
227 | if (8 >= beforePeriod && 3 >= afterPeriod) | 237 | if (8 >= beforePeriod && 3 >= afterPeriod) |
228 | { | 238 | { |
229 | return true; | 239 | return true; |
230 | } | ||
231 | } | 240 | } |
232 | 241 | ||
233 | return false; | 242 | return false; |
234 | } | 243 | } |
235 | else | 244 | else |
236 | { | 245 | { |
237 | return Common.LegalShortFilename.IsMatch(filename); | 246 | if (filename.Length > 12) |
247 | { | ||
248 | return false; | ||
249 | } | ||
250 | |||
251 | var expectedDot = filename.IndexOfAny(IllegalShortFilenameCharacters); | ||
252 | if (expectedDot == -1) | ||
253 | { | ||
254 | return filename.Length < 9; | ||
255 | } | ||
256 | else if (expectedDot > 8 || filename[expectedDot] != '.') | ||
257 | { | ||
258 | return false; | ||
259 | } | ||
260 | |||
261 | var validExtension = filename.IndexOfAny(IllegalShortFilenameCharacters, expectedDot + 1); | ||
262 | return validExtension == -1; | ||
238 | } | 263 | } |
239 | } | 264 | } |
240 | 265 | ||
241 | /// <summary> | 266 | /// <summary> |
242 | /// Verifies if an identifier is a valid binder variable name. | 267 | /// Verifies if an identifier is a valid binder variable name. |
243 | /// </summary> | 268 | /// </summary> |
244 | /// <param name="name">Binder variable name to verify.</param> | 269 | /// <param name="variable">Binder variable name to verify.</param> |
245 | /// <returns>True if the identifier is a valid binder variable name.</returns> | 270 | /// <returns>True if the identifier is a valid binder variable name.</returns> |
246 | public static bool IsValidBinderVariable(string name) | 271 | public static bool IsValidBinderVariable(string variable) |
247 | { | 272 | { |
248 | if (String.IsNullOrEmpty(name)) | 273 | return TryParseWixVariable(variable, 0, out var parsed) && parsed.Index == 0 && parsed.Length == variable.Length && (parsed.Namespace == "bind" || parsed.Namespace == "wix"); |
249 | { | ||
250 | return false; | ||
251 | } | ||
252 | |||
253 | Match match = Common.WixVariableRegex.Match(name); | ||
254 | |||
255 | return (match.Success && ("bind" == match.Groups["namespace"].Value || "wix" == match.Groups["namespace"].Value) && 0 == match.Index && name.Length == match.Length); | ||
256 | } | 274 | } |
257 | 275 | ||
258 | /// <summary> | 276 | /// <summary> |
259 | /// Verifies if a string contains a valid binder variable name. | 277 | /// Verifies if a string contains a valid binder variable name. |
260 | /// </summary> | 278 | /// </summary> |
261 | /// <param name="name">String to verify.</param> | 279 | /// <param name="verify">String to verify.</param> |
262 | /// <returns>True if the string contains a valid binder variable name.</returns> | 280 | /// <returns>True if the string contains a valid binder variable name.</returns> |
263 | public static bool ContainsValidBinderVariable(string name) | 281 | public static bool ContainsValidBinderVariable(string verify) |
264 | { | 282 | { |
265 | if (String.IsNullOrEmpty(name)) | 283 | return TryParseWixVariable(verify, 0, out var parsed) && (parsed.Namespace == "bind" || parsed.Namespace == "wix"); |
266 | { | ||
267 | return false; | ||
268 | } | ||
269 | |||
270 | Match match = Common.WixVariableRegex.Match(name); | ||
271 | |||
272 | return match.Success && ("bind" == match.Groups["namespace"].Value || "wix" == match.Groups["namespace"].Value); | ||
273 | } | 284 | } |
274 | 285 | ||
275 | /// <summary> | 286 | /// <summary> |
@@ -281,7 +292,7 @@ namespace WixToolset.Core | |||
281 | { | 292 | { |
282 | if (!Common.IsValidBinderVariable(version)) | 293 | if (!Common.IsValidBinderVariable(version)) |
283 | { | 294 | { |
284 | Version ver = null; | 295 | Version ver; |
285 | 296 | ||
286 | try | 297 | try |
287 | { | 298 | { |
@@ -344,16 +355,31 @@ namespace WixToolset.Core | |||
344 | /// <returns>A version of the name that is a legal identifier.</returns> | 355 | /// <returns>A version of the name that is a legal identifier.</returns> |
345 | internal static string GetIdentifierFromName(string name) | 356 | internal static string GetIdentifierFromName(string name) |
346 | { | 357 | { |
347 | string result = IllegalIdentifierCharacters.Replace(name, "_"); // replace illegal characters with "_". | 358 | StringBuilder sb = null; |
359 | var offset = 0; | ||
348 | 360 | ||
349 | // MSI identifiers must begin with an alphabetic character or an | 361 | // MSI identifiers must begin with an alphabetic character or an |
350 | // underscore. Prefix all other values with an underscore. | 362 | // underscore. Prefix all other values with an underscore. |
351 | if (AddPrefix.IsMatch(name)) | 363 | if (!ValidIdentifierChar(name[0], true)) |
352 | { | 364 | { |
353 | result = String.Concat("_", result); | 365 | sb = new StringBuilder("_" + name); |
366 | offset = 1; | ||
354 | } | 367 | } |
355 | 368 | ||
356 | return result; | 369 | for (var i = 0; i < name.Length; ++i) |
370 | { | ||
371 | if (!ValidIdentifierChar(name[i], false)) | ||
372 | { | ||
373 | if (sb == null) | ||
374 | { | ||
375 | sb = new StringBuilder(name); | ||
376 | } | ||
377 | |||
378 | sb[i + offset] = '_'; | ||
379 | } | ||
380 | } | ||
381 | |||
382 | return sb?.ToString() ?? name; | ||
357 | } | 383 | } |
358 | 384 | ||
359 | /// <summary> | 385 | /// <summary> |
@@ -363,7 +389,28 @@ namespace WixToolset.Core | |||
363 | /// <returns>True if a property is found in the string.</returns> | 389 | /// <returns>True if a property is found in the string.</returns> |
364 | internal static bool ContainsProperty(string possibleProperty) | 390 | internal static bool ContainsProperty(string possibleProperty) |
365 | { | 391 | { |
366 | return PropertySearch.IsMatch(possibleProperty); | 392 | var start = possibleProperty.IndexOf('['); |
393 | if (start != -1 && start < possibleProperty.Length - 2) | ||
394 | { | ||
395 | var end = possibleProperty.IndexOf(']', start + 1); | ||
396 | if (end > start + 1) | ||
397 | { | ||
398 | // Skip supported property modifiers. | ||
399 | if (possibleProperty[start + 1] == '#' || possibleProperty[start + 1] == '$' || possibleProperty[start + 1] == '!') | ||
400 | { | ||
401 | ++start; | ||
402 | } | ||
403 | |||
404 | var id = possibleProperty.Substring(start + 1, end - 1); | ||
405 | |||
406 | if (Common.IsIdentifier(id)) | ||
407 | { | ||
408 | return true; | ||
409 | } | ||
410 | } | ||
411 | } | ||
412 | |||
413 | return false; | ||
367 | } | 414 | } |
368 | 415 | ||
369 | /// <summary> | 416 | /// <summary> |
@@ -436,7 +483,7 @@ namespace WixToolset.Core | |||
436 | public static string[] GetNames(string value) | 483 | public static string[] GetNames(string value) |
437 | { | 484 | { |
438 | string[] names = new string[4]; | 485 | string[] names = new string[4]; |
439 | int targetSeparator = value.IndexOf(":", StringComparison.Ordinal); | 486 | int targetSeparator = value.IndexOf(':'); |
440 | 487 | ||
441 | // split source and target | 488 | // split source and target |
442 | string sourceName = null; | 489 | string sourceName = null; |
@@ -451,7 +498,7 @@ namespace WixToolset.Core | |||
451 | string sourceLongName = null; | 498 | string sourceLongName = null; |
452 | if (null != sourceName) | 499 | if (null != sourceName) |
453 | { | 500 | { |
454 | int sourceLongNameSeparator = sourceName.IndexOf("|", StringComparison.Ordinal); | 501 | int sourceLongNameSeparator = sourceName.IndexOf('|'); |
455 | if (0 <= sourceLongNameSeparator) | 502 | if (0 <= sourceLongNameSeparator) |
456 | { | 503 | { |
457 | sourceLongName = sourceName.Substring(sourceLongNameSeparator + 1); | 504 | sourceLongName = sourceName.Substring(sourceLongNameSeparator + 1); |
@@ -460,7 +507,7 @@ namespace WixToolset.Core | |||
460 | } | 507 | } |
461 | 508 | ||
462 | // split the target short and long names | 509 | // split the target short and long names |
463 | int targetLongNameSeparator = targetName.IndexOf("|", StringComparison.Ordinal); | 510 | int targetLongNameSeparator = targetName.IndexOf('|'); |
464 | string targetLongName = null; | 511 | string targetLongName = null; |
465 | if (0 <= targetLongNameSeparator) | 512 | if (0 <= targetLongNameSeparator) |
466 | { | 513 | { |
@@ -555,7 +602,7 @@ namespace WixToolset.Core | |||
555 | /// <returns>The attribute's value.</returns> | 602 | /// <returns>The attribute's value.</returns> |
556 | internal static string GetAttributeValue(IMessaging messaging, SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule) | 603 | internal static string GetAttributeValue(IMessaging messaging, SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule) |
557 | { | 604 | { |
558 | string value = attribute.Value; | 605 | var value = attribute.Value; |
559 | 606 | ||
560 | if ((emptyRule == EmptyRule.MustHaveNonWhitespaceCharacters && String.IsNullOrEmpty(value.Trim())) || | 607 | if ((emptyRule == EmptyRule.MustHaveNonWhitespaceCharacters && String.IsNullOrEmpty(value.Trim())) || |
561 | (emptyRule == EmptyRule.CanBeWhitespaceOnly && String.IsNullOrEmpty(value))) | 608 | (emptyRule == EmptyRule.CanBeWhitespaceOnly && String.IsNullOrEmpty(value))) |
@@ -574,15 +621,20 @@ namespace WixToolset.Core | |||
574 | /// <returns>true if the value is an identifier; false otherwise.</returns> | 621 | /// <returns>true if the value is an identifier; false otherwise.</returns> |
575 | public static bool IsIdentifier(string value) | 622 | public static bool IsIdentifier(string value) |
576 | { | 623 | { |
577 | if (!String.IsNullOrEmpty(value)) | 624 | if (String.IsNullOrEmpty(value)) |
625 | { | ||
626 | return false; | ||
627 | } | ||
628 | |||
629 | for (var i = 0; i < value.Length; ++i) | ||
578 | { | 630 | { |
579 | if (LegalIdentifierCharacters.IsMatch(value)) | 631 | if (!ValidIdentifierChar(value[i], i == 0)) |
580 | { | 632 | { |
581 | return true; | 633 | return false; |
582 | } | 634 | } |
583 | } | 635 | } |
584 | 636 | ||
585 | return false; | 637 | return true; |
586 | } | 638 | } |
587 | 639 | ||
588 | /// <summary> | 640 | /// <summary> |
@@ -698,6 +750,85 @@ namespace WixToolset.Core | |||
698 | return text?.Value; | 750 | return text?.Value; |
699 | } | 751 | } |
700 | 752 | ||
753 | internal static bool TryParseWixVariable(string value, int start, out ParsedWixVariable parsedVariable) | ||
754 | { | ||
755 | parsedVariable = null; | ||
756 | |||
757 | if (String.IsNullOrEmpty(value) || start >= value.Length) | ||
758 | { | ||
759 | return false; | ||
760 | } | ||
761 | |||
762 | var startWixVariable = value.IndexOf("!(", start, StringComparison.Ordinal); | ||
763 | if (startWixVariable == -1) | ||
764 | { | ||
765 | return false; | ||
766 | } | ||
767 | |||
768 | var firstDot = value.IndexOf('.', startWixVariable + 1); | ||
769 | if (firstDot == -1) | ||
770 | { | ||
771 | return false; | ||
772 | } | ||
773 | |||
774 | var ns = value.Substring(startWixVariable + 2, firstDot - startWixVariable - 2); | ||
775 | if (ns != "loc" && ns != "bind" && ns != "wix") | ||
776 | { | ||
777 | return false; | ||
778 | } | ||
779 | |||
780 | var closeParen = value.IndexOf(')', firstDot); | ||
781 | if (closeParen == -1) | ||
782 | { | ||
783 | return false; | ||
784 | } | ||
785 | |||
786 | string name; | ||
787 | string scope = null; | ||
788 | string defaultValue = null; | ||
789 | |||
790 | var secondDot = value.IndexOf('.', firstDot + 1, closeParen - firstDot); | ||
791 | var equalsDefaultValue = value.IndexOf('=', firstDot + 1, closeParen - firstDot); | ||
792 | var end = equalsDefaultValue == -1 ? closeParen : equalsDefaultValue; | ||
793 | |||
794 | if (secondDot == -1) | ||
795 | { | ||
796 | name = value.Substring(firstDot + 1, end - firstDot - 1); | ||
797 | } | ||
798 | else | ||
799 | { | ||
800 | name = value.Substring(firstDot + 1, secondDot - firstDot - 1); | ||
801 | scope = value.Substring(secondDot + 1, end - secondDot - 1); | ||
802 | |||
803 | if (!Common.IsIdentifier(scope)) | ||
804 | { | ||
805 | return false; | ||
806 | } | ||
807 | } | ||
808 | |||
809 | if (!Common.IsIdentifier(name)) | ||
810 | { | ||
811 | return false; | ||
812 | } | ||
813 | |||
814 | if (equalsDefaultValue != -1 && equalsDefaultValue < closeParen) | ||
815 | { | ||
816 | defaultValue = value.Substring(equalsDefaultValue + 1, end - equalsDefaultValue - 1); | ||
817 | } | ||
818 | |||
819 | parsedVariable = new ParsedWixVariable | ||
820 | { | ||
821 | Index = startWixVariable, | ||
822 | Length = closeParen - startWixVariable + 1, | ||
823 | Namespace = ns, | ||
824 | Name = name, | ||
825 | Scope = scope, | ||
826 | DefaultValue = defaultValue | ||
827 | }; | ||
828 | |||
829 | return true; | ||
830 | } | ||
831 | |||
701 | /// <summary> | 832 | /// <summary> |
702 | /// Display an unexpected attribute error. | 833 | /// Display an unexpected attribute error. |
703 | /// </summary> | 834 | /// </summary> |
@@ -727,5 +858,11 @@ namespace WixToolset.Core | |||
727 | messaging.Write(ErrorMessages.UnsupportedExtensionAttribute(sourceLineNumbers, extensionAttribute.Parent.Name.LocalName, extensionAttribute.Name.LocalName)); | 858 | messaging.Write(ErrorMessages.UnsupportedExtensionAttribute(sourceLineNumbers, extensionAttribute.Parent.Name.LocalName, extensionAttribute.Name.LocalName)); |
728 | } | 859 | } |
729 | } | 860 | } |
861 | |||
862 | private static bool ValidIdentifierChar(char c, bool firstChar) | ||
863 | { | ||
864 | return ('A' <= c && 'Z' >= c) || ('a' <= c && 'z' >= c) || '_' == c || | ||
865 | (!firstChar && (Char.IsDigit(c) || '.' == c)); | ||
866 | } | ||
730 | } | 867 | } |
731 | } | 868 | } |
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs index b29821b0..020e35fe 100644 --- a/src/WixToolset.Core/Compiler.cs +++ b/src/WixToolset.Core/Compiler.cs | |||
@@ -9,7 +9,6 @@ namespace WixToolset.Core | |||
9 | using System.Globalization; | 9 | using System.Globalization; |
10 | using System.IO; | 10 | using System.IO; |
11 | using System.Linq; | 11 | using System.Linq; |
12 | using System.Text.RegularExpressions; | ||
13 | using System.Xml.Linq; | 12 | using System.Xml.Linq; |
14 | using WixToolset.Data; | 13 | using WixToolset.Data; |
15 | using WixToolset.Data.Symbols; | 14 | using WixToolset.Data.Symbols; |
@@ -26,8 +25,8 @@ namespace WixToolset.Core | |||
26 | private const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB | 25 | private const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB |
27 | private const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) | 26 | private const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) |
28 | 27 | ||
29 | private const string DefaultComponentIdPlaceholderFormat = "WixComponentIdPlaceholder{0}"; | 28 | private const string DefaultComponentIdPlaceholderPrefix = "WixComponentIdPlaceholder"; |
30 | private const string DefaultComponentIdPlaceholderWixVariableFormat = "!(wix.{0})"; | 29 | private const string DefaultComponentIdPlaceholderWixVariablePrefix = "!(wix."; |
31 | // If these are true you know you are building a module or product | 30 | // If these are true you know you are building a module or product |
32 | // but if they are false you cannot not be sure they will not end | 31 | // but if they are false you cannot not be sure they will not end |
33 | // up a product or module. Use these flags carefully. | 32 | // up a product or module. Use these flags carefully. |
@@ -342,16 +341,22 @@ namespace WixToolset.Core | |||
342 | 341 | ||
343 | if (!String.IsNullOrEmpty(value)) | 342 | if (!String.IsNullOrEmpty(value)) |
344 | { | 343 | { |
345 | var regex = new Regex(@"\[(?<identifier>[a-zA-Z_][a-zA-Z0-9_\.]*)]", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture); | 344 | var start = value.IndexOf('['); |
346 | var matches = regex.Matches(value); | 345 | while (start != -1 && start < value.Length) |
347 | |||
348 | foreach (Match match in matches) | ||
349 | { | 346 | { |
350 | var group = match.Groups["identifier"]; | 347 | var end = value.IndexOf(']', start + 1); |
351 | if (group.Success) | 348 | if (end == -1) |
349 | { | ||
350 | break; | ||
351 | } | ||
352 | |||
353 | var id = value.Substring(start + 1, end - 1); | ||
354 | if (Common.IsIdentifier(id)) | ||
352 | { | 355 | { |
353 | this.Core.Write(WarningMessages.PropertyValueContainsPropertyReference(sourceLineNumbers, propertyId.Id, group.Value)); | 356 | this.Core.Write(WarningMessages.PropertyValueContainsPropertyReference(sourceLineNumbers, propertyId.Id, id)); |
354 | } | 357 | } |
358 | |||
359 | start = (end < value.Length) ? value.IndexOf('[', end + 1) : -1; | ||
355 | } | 360 | } |
356 | } | 361 | } |
357 | 362 | ||
@@ -2091,8 +2096,8 @@ namespace WixToolset.Core | |||
2091 | var encounteredODBCDataSource = false; | 2096 | var encounteredODBCDataSource = false; |
2092 | var files = 0; | 2097 | var files = 0; |
2093 | var guid = "*"; | 2098 | var guid = "*"; |
2094 | var componentIdPlaceholder = String.Format(Compiler.DefaultComponentIdPlaceholderFormat, this.componentIdPlaceholdersResolver.VariableCount); // placeholder id for defaulting Component/@Id to keypath id. | 2099 | var componentIdPlaceholder = Compiler.DefaultComponentIdPlaceholderPrefix + this.componentIdPlaceholdersResolver.VariableCount; // placeholder id for defaulting Component/@Id to keypath id. |
2095 | var componentIdPlaceholderWixVariable = String.Format(Compiler.DefaultComponentIdPlaceholderWixVariableFormat, componentIdPlaceholder); | 2100 | var componentIdPlaceholderWixVariable = Compiler.DefaultComponentIdPlaceholderWixVariablePrefix + componentIdPlaceholder + ")"; |
2096 | var id = new Identifier(AccessModifier.Private, componentIdPlaceholderWixVariable); | 2101 | var id = new Identifier(AccessModifier.Private, componentIdPlaceholderWixVariable); |
2097 | var keyFound = false; | 2102 | var keyFound = false; |
2098 | string keyPath = null; | 2103 | string keyPath = null; |
@@ -4080,7 +4085,7 @@ namespace WixToolset.Core | |||
4080 | break; | 4085 | break; |
4081 | case "Name": | 4086 | case "Name": |
4082 | nameHasValue = true; | 4087 | nameHasValue = true; |
4083 | if (attrib.Value.Equals(".")) | 4088 | if (attrib.Value == ".") |
4084 | { | 4089 | { |
4085 | name = attrib.Value; | 4090 | name = attrib.Value; |
4086 | } | 4091 | } |
@@ -4225,7 +4230,7 @@ namespace WixToolset.Core | |||
4225 | defaultDir = String.Concat(defaultDir, ":", String.IsNullOrEmpty(shortSourceName) ? sourceName : String.Concat(shortSourceName, "|", sourceName)); | 4230 | defaultDir = String.Concat(defaultDir, ":", String.IsNullOrEmpty(shortSourceName) ? sourceName : String.Concat(shortSourceName, "|", sourceName)); |
4226 | } | 4231 | } |
4227 | 4232 | ||
4228 | if ("TARGETDIR".Equals(id.Id) && !"SourceDir".Equals(defaultDir)) | 4233 | if ("TARGETDIR".Equals(id.Id, StringComparison.Ordinal) && !"SourceDir".Equals(defaultDir, StringComparison.Ordinal)) |
4229 | { | 4234 | { |
4230 | this.Core.Write(ErrorMessages.IllegalTargetDirDefaultDir(sourceLineNumbers, defaultDir)); | 4235 | this.Core.Write(ErrorMessages.IllegalTargetDirDefaultDir(sourceLineNumbers, defaultDir)); |
4231 | } | 4236 | } |
@@ -7147,13 +7152,9 @@ namespace WixToolset.Core | |||
7147 | else // external cabinet file | 7152 | else // external cabinet file |
7148 | { | 7153 | { |
7149 | // external cabinet files must use 8.3 filenames | 7154 | // external cabinet files must use 8.3 filenames |
7150 | if (!String.IsNullOrEmpty(cabinet) && !this.Core.IsValidShortFilename(cabinet, false)) | 7155 | if (!String.IsNullOrEmpty(cabinet) && !this.Core.IsValidLongFilename(cabinet) && !Common.ContainsValidBinderVariable(cabinet)) |
7151 | { | 7156 | { |
7152 | // WiX variables in the name will trip the "not a valid 8.3 name" switch, so let them through | 7157 | this.Core.Write(WarningMessages.MediaExternalCabinetFilenameIllegal(sourceLineNumbers, node.Name.LocalName, "Cabinet", cabinet)); |
7153 | if (!Common.WixVariableRegex.Match(cabinet).Success) | ||
7154 | { | ||
7155 | this.Core.Write(WarningMessages.MediaExternalCabinetFilenameIllegal(sourceLineNumbers, node.Name.LocalName, "Cabinet", cabinet)); | ||
7156 | } | ||
7157 | } | 7158 | } |
7158 | } | 7159 | } |
7159 | } | 7160 | } |
@@ -7286,12 +7287,12 @@ namespace WixToolset.Core | |||
7286 | if (!this.Core.IsValidLocIdentifier(exampleCabinetName)) | 7287 | if (!this.Core.IsValidLocIdentifier(exampleCabinetName)) |
7287 | { | 7288 | { |
7288 | // The example name should not match the authored template since that would nullify the | 7289 | // The example name should not match the authored template since that would nullify the |
7289 | // reason for having multiple cabients. External cabinet files must also be valid file names. | 7290 | // reason for having multiple cabinets. External cabinet files must also be valid file names. |
7290 | if (exampleCabinetName.Equals(authoredCabinetTemplateValue) || !this.Core.IsValidLongFilename(exampleCabinetName, false)) | 7291 | if (exampleCabinetName.Equals(authoredCabinetTemplateValue, StringComparison.OrdinalIgnoreCase) || !this.Core.IsValidLongFilename(exampleCabinetName, false)) |
7291 | { | 7292 | { |
7292 | this.Core.Write(ErrorMessages.InvalidCabinetTemplate(sourceLineNumbers, cabinetTemplate)); | 7293 | this.Core.Write(ErrorMessages.InvalidCabinetTemplate(sourceLineNumbers, cabinetTemplate)); |
7293 | } | 7294 | } |
7294 | else if (!this.Core.IsValidShortFilename(exampleCabinetName, false) && !Common.WixVariableRegex.Match(exampleCabinetName).Success) // ignore short names with wix variables because it rarely works out. | 7295 | else if (!this.Core.IsValidLongFilename(exampleCabinetName) && !Common.ContainsValidBinderVariable(exampleCabinetName)) // ignore short names with wix variables because it rarely works out. |
7295 | { | 7296 | { |
7296 | this.Core.Write(WarningMessages.MediaExternalCabinetFilenameIllegal(sourceLineNumbers, node.Name.LocalName, "CabinetTemplate", cabinetTemplate)); | 7297 | this.Core.Write(WarningMessages.MediaExternalCabinetFilenameIllegal(sourceLineNumbers, node.Name.LocalName, "CabinetTemplate", cabinetTemplate)); |
7297 | } | 7298 | } |
diff --git a/src/WixToolset.Core/CompilerCore.cs b/src/WixToolset.Core/CompilerCore.cs index c2724f6b..2f65db7a 100644 --- a/src/WixToolset.Core/CompilerCore.cs +++ b/src/WixToolset.Core/CompilerCore.cs | |||
@@ -10,7 +10,6 @@ namespace WixToolset.Core | |||
10 | using System.Globalization; | 10 | using System.Globalization; |
11 | using System.Reflection; | 11 | using System.Reflection; |
12 | using System.Text; | 12 | using System.Text; |
13 | using System.Text.RegularExpressions; | ||
14 | using System.Xml.Linq; | 13 | using System.Xml.Linq; |
15 | using WixToolset.Data; | 14 | using WixToolset.Data; |
16 | using WixToolset.Data.Symbols; | 15 | using WixToolset.Data.Symbols; |
@@ -45,16 +44,8 @@ namespace WixToolset.Core | |||
45 | internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/"; | 44 | internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/"; |
46 | internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; | 45 | internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; |
47 | 46 | ||
48 | private static readonly Regex AmbiguousFilename = new Regex(@"^.{6}\~\d", RegexOptions.Compiled); | ||
49 | |||
50 | private const string IllegalLongFilenameCharacters = @"[\\\?|><:/\*""]"; // illegal: \ ? | > < : / * " | ||
51 | private static readonly Regex IllegalLongFilename = new Regex(IllegalLongFilenameCharacters, RegexOptions.Compiled); | ||
52 | |||
53 | //public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB | ||
54 | |||
55 | |||
56 | // Built-in variables (from burn\engine\variable.cpp, "vrgBuiltInVariables", around line 113) | 47 | // Built-in variables (from burn\engine\variable.cpp, "vrgBuiltInVariables", around line 113) |
57 | private static readonly List<String> BuiltinBundleVariables = new List<string>( | 48 | private static readonly List<string> BuiltinBundleVariables = new List<string>( |
58 | new string[] { | 49 | new string[] { |
59 | "AdminToolsFolder", | 50 | "AdminToolsFolder", |
60 | "AppDataFolder", | 51 | "AppDataFolder", |
@@ -221,7 +212,13 @@ namespace WixToolset.Core | |||
221 | /// <returns>true if the filename is ambiguous; false otherwise.</returns> | 212 | /// <returns>true if the filename is ambiguous; false otherwise.</returns> |
222 | public static bool IsAmbiguousFilename(string filename) | 213 | public static bool IsAmbiguousFilename(string filename) |
223 | { | 214 | { |
224 | return String.IsNullOrEmpty(filename) ? false : CompilerCore.AmbiguousFilename.IsMatch(filename); | 215 | if (String.IsNullOrEmpty(filename)) |
216 | { | ||
217 | return false; | ||
218 | } | ||
219 | |||
220 | var tilde = filename.IndexOf('~'); | ||
221 | return (tilde > 0 && tilde < filename.Length) && Char.IsNumber(filename[tilde + 1]); | ||
225 | } | 222 | } |
226 | 223 | ||
227 | /// <summary> | 224 | /// <summary> |
@@ -273,9 +270,29 @@ namespace WixToolset.Core | |||
273 | /// <param name="filename">Filename to make valid.</param> | 270 | /// <param name="filename">Filename to make valid.</param> |
274 | /// <param name="replace">Replacement string for invalid characters in filename.</param> | 271 | /// <param name="replace">Replacement string for invalid characters in filename.</param> |
275 | /// <returns>Valid filename.</returns> | 272 | /// <returns>Valid filename.</returns> |
276 | public static string MakeValidLongFileName(string filename, string replace) | 273 | public static string MakeValidLongFileName(string filename, char replace) |
277 | { | 274 | { |
278 | return CompilerCore.IllegalLongFilename.Replace(filename, replace); | 275 | if (String.IsNullOrEmpty(filename)) |
276 | { | ||
277 | return filename; | ||
278 | } | ||
279 | |||
280 | StringBuilder sb = null; | ||
281 | |||
282 | var found = filename.IndexOfAny(Common.IllegalLongFilenameCharacters); | ||
283 | while (found != -1) | ||
284 | { | ||
285 | if (sb == null) | ||
286 | { | ||
287 | sb = new StringBuilder(filename); | ||
288 | } | ||
289 | |||
290 | sb[found] = replace; | ||
291 | |||
292 | found = (found + 1 < filename.Length) ? filename.IndexOfAny(Common.IllegalLongFilenameCharacters, found + 1) : -1; | ||
293 | } | ||
294 | |||
295 | return sb?.ToString() ?? filename; | ||
279 | } | 296 | } |
280 | 297 | ||
281 | /// <summary> | 298 | /// <summary> |
@@ -717,7 +734,7 @@ namespace WixToolset.Core | |||
717 | throw new ArgumentNullException("attribute"); | 734 | throw new ArgumentNullException("attribute"); |
718 | } | 735 | } |
719 | 736 | ||
720 | string value = this.GetAttributeValue(sourceLineNumbers, attribute); | 737 | var value = this.GetAttributeValue(sourceLineNumbers, attribute); |
721 | 738 | ||
722 | if (0 < value.Length) | 739 | if (0 < value.Length) |
723 | { | 740 | { |
@@ -1039,16 +1056,6 @@ namespace WixToolset.Core | |||
1039 | return this.parseHelper.ScheduleActionSymbol(this.ActiveSection, sourceLineNumbers, access, sequence, actionName, condition, beforeAction, afterAction, overridable); | 1056 | return this.parseHelper.ScheduleActionSymbol(this.ActiveSection, sourceLineNumbers, access, sequence, actionName, condition, beforeAction, afterAction, overridable); |
1040 | } | 1057 | } |
1041 | 1058 | ||
1042 | /// <summary> | ||
1043 | /// Finds a compiler extension by namespace URI. | ||
1044 | /// </summary> | ||
1045 | /// <param name="ns">Namespace the extension supports.</param> | ||
1046 | /// <returns>True if found compiler extension or false if nothing matches namespace URI.</returns> | ||
1047 | private bool TryFindExtension(XNamespace ns, out ICompilerExtension extension) | ||
1048 | { | ||
1049 | return this.extensions.TryGetValue(ns, out extension); | ||
1050 | } | ||
1051 | |||
1052 | private static string CreateValueList(ValueListKind kind, IEnumerable<string> values) | 1059 | private static string CreateValueList(ValueListKind kind, IEnumerable<string> values) |
1053 | { | 1060 | { |
1054 | // Ideally, we could denote the list kind (and the list itself) directly in the | 1061 | // Ideally, we could denote the list kind (and the list itself) directly in the |
diff --git a/src/WixToolset.Core/Compiler_2.cs b/src/WixToolset.Core/Compiler_2.cs index 7e2485e1..29f240f4 100644 --- a/src/WixToolset.Core/Compiler_2.cs +++ b/src/WixToolset.Core/Compiler_2.cs | |||
@@ -4182,30 +4182,6 @@ namespace WixToolset.Core | |||
4182 | this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Show", showValue, "normal", "maximized", "minimized")); | 4182 | this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Show", showValue, "normal", "maximized", "minimized")); |
4183 | break; | 4183 | break; |
4184 | } | 4184 | } |
4185 | //if (showValue.Length == 0) | ||
4186 | //{ | ||
4187 | // show = CompilerConstants.IllegalInteger; | ||
4188 | //} | ||
4189 | //else | ||
4190 | //{ | ||
4191 | // var showType = Wix.Shortcut.ParseShowType(showValue); | ||
4192 | // switch (showType) | ||
4193 | // { | ||
4194 | // case Wix.Shortcut.ShowType.normal: | ||
4195 | // show = 1; | ||
4196 | // break; | ||
4197 | // case Wix.Shortcut.ShowType.maximized: | ||
4198 | // show = 3; | ||
4199 | // break; | ||
4200 | // case Wix.Shortcut.ShowType.minimized: | ||
4201 | // show = 7; | ||
4202 | // break; | ||
4203 | // default: | ||
4204 | // this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Show", showValue, "normal", "maximized", "minimized")); | ||
4205 | // show = CompilerConstants.IllegalInteger; | ||
4206 | // break; | ||
4207 | // } | ||
4208 | //} | ||
4209 | break; | 4185 | break; |
4210 | case "Target": | 4186 | case "Target": |
4211 | target = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | 4187 | target = this.Core.GetAttributeValue(sourceLineNumbers, attrib); |
@@ -4325,11 +4301,11 @@ namespace WixToolset.Core | |||
4325 | } | 4301 | } |
4326 | else if ("Component" == parentElementLocalName || "CreateFolder" == parentElementLocalName) | 4302 | else if ("Component" == parentElementLocalName || "CreateFolder" == parentElementLocalName) |
4327 | { | 4303 | { |
4328 | target = String.Format(CultureInfo.InvariantCulture, "[{0}]", defaultTarget); | 4304 | target = "[" + defaultTarget + "]"; |
4329 | } | 4305 | } |
4330 | else if ("File" == parentElementLocalName) | 4306 | else if ("File" == parentElementLocalName) |
4331 | { | 4307 | { |
4332 | target = String.Format(CultureInfo.InvariantCulture, "[#{0}]", defaultTarget); | 4308 | target = "[#" + defaultTarget + "]"; |
4333 | } | 4309 | } |
4334 | 4310 | ||
4335 | this.Core.AddSymbol(new ShortcutSymbol(sourceLineNumbers, id) | 4311 | this.Core.AddSymbol(new ShortcutSymbol(sourceLineNumbers, id) |
diff --git a/src/WixToolset.Core/Compiler_Bundle.cs b/src/WixToolset.Core/Compiler_Bundle.cs index 578c7dcd..2089f037 100644 --- a/src/WixToolset.Core/Compiler_Bundle.cs +++ b/src/WixToolset.Core/Compiler_Bundle.cs | |||
@@ -236,7 +236,7 @@ namespace WixToolset.Core | |||
236 | else | 236 | else |
237 | { | 237 | { |
238 | // Ensure only allowable path characters are in "name" (and change spaces to underscores). | 238 | // Ensure only allowable path characters are in "name" (and change spaces to underscores). |
239 | fileSystemSafeBundleName = CompilerCore.MakeValidLongFileName(name.Replace(' ', '_'), "_"); | 239 | fileSystemSafeBundleName = CompilerCore.MakeValidLongFileName(name.Replace(' ', '_'), '_'); |
240 | logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ":log"); | 240 | logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ":log"); |
241 | } | 241 | } |
242 | 242 | ||
diff --git a/src/WixToolset.Core/Compiler_UI.cs b/src/WixToolset.Core/Compiler_UI.cs index 36d2e4e9..9353d966 100644 --- a/src/WixToolset.Core/Compiler_UI.cs +++ b/src/WixToolset.Core/Compiler_UI.cs | |||
@@ -1740,7 +1740,7 @@ namespace WixToolset.Core | |||
1740 | { | 1740 | { |
1741 | // if we're not looking at a standard action or a formatted string then create a reference | 1741 | // if we're not looking at a standard action or a formatted string then create a reference |
1742 | // to the custom action. | 1742 | // to the custom action. |
1743 | if (!WindowsInstallerStandard.IsStandardAction(argument) && !Common.ContainsProperty(argument)) | 1743 | if (!WindowsInstallerStandard.IsStandardAction(argument) && !this.Core.ContainsProperty(argument)) |
1744 | { | 1744 | { |
1745 | this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.CustomAction, argument); | 1745 | this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.CustomAction, argument); |
1746 | } | 1746 | } |
diff --git a/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs index db465354..81a18e24 100644 --- a/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs +++ b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs | |||
@@ -9,7 +9,6 @@ namespace WixToolset.Core.ExtensibilityServices | |||
9 | using System.IO; | 9 | using System.IO; |
10 | using System.Security.Cryptography; | 10 | using System.Security.Cryptography; |
11 | using System.Text; | 11 | using System.Text; |
12 | using System.Text.RegularExpressions; | ||
13 | using System.Xml.Linq; | 12 | using System.Xml.Linq; |
14 | using WixToolset.Data; | 13 | using WixToolset.Data; |
15 | using WixToolset.Data.Symbols; | 14 | using WixToolset.Data.Symbols; |
@@ -20,19 +19,6 @@ namespace WixToolset.Core.ExtensibilityServices | |||
20 | 19 | ||
21 | internal class ParseHelper : IParseHelper | 20 | internal class ParseHelper : IParseHelper |
22 | { | 21 | { |
23 | private const string LegalLongFilenameCharacters = @"[^\\\?|><:/\*""]"; // opposite of illegal above. | ||
24 | private static readonly Regex LegalLongFilename = new Regex(String.Concat("^", LegalLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled); | ||
25 | |||
26 | private const string LegalRelativeLongFilenameCharacters = @"[^\?|><:/\*""]"; // (like legal long, but we allow '\') illegal: ? | > < : / * " | ||
27 | private static readonly Regex LegalRelativeLongFilename = new Regex(String.Concat("^", LegalRelativeLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled); | ||
28 | |||
29 | private const string LegalWildcardLongFilenameCharacters = @"[^\\|><:/""]"; // illegal: \ | > < : / " | ||
30 | private static readonly Regex LegalWildcardLongFilename = new Regex(String.Concat("^", LegalWildcardLongFilenameCharacters, @"{1,259}$")); | ||
31 | |||
32 | private static readonly Regex LegalIdentifierWithAccess = new Regex(@"^((?<access>public|internal|protected|private)\s+)?(?<id>[_A-Za-z][0-9A-Za-z_\.]*)$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); | ||
33 | |||
34 | private static readonly Regex PutGuidHere = new Regex(@"PUT\-GUID\-(?:\d+\-)?HERE", RegexOptions.Singleline); | ||
35 | |||
36 | public ParseHelper(IWixToolsetServiceProvider serviceProvider) | 22 | public ParseHelper(IWixToolsetServiceProvider serviceProvider) |
37 | { | 23 | { |
38 | this.ServiceProvider = serviceProvider; | 24 | this.ServiceProvider = serviceProvider; |
@@ -119,7 +105,7 @@ namespace WixToolset.Core.ExtensibilityServices | |||
119 | id = parentId; | 105 | id = parentId; |
120 | 106 | ||
121 | var pathStartsAt = 0; | 107 | var pathStartsAt = 0; |
122 | if (inlineSyntax[0].EndsWith(":")) | 108 | if (inlineSyntax[0].EndsWith(":", StringComparison.Ordinal)) |
123 | { | 109 | { |
124 | // TODO: should overriding the parent identifier with a specific id be an error or a warning or just let it slide? | 110 | // TODO: should overriding the parent identifier with a specific id be an error or a warning or just let it slide? |
125 | //if (null != parentId) | 111 | //if (null != parentId) |
@@ -415,44 +401,34 @@ namespace WixToolset.Core.ExtensibilityServices | |||
415 | var emptyRule = canBeEmpty ? EmptyRule.CanBeEmpty : EmptyRule.CanBeWhitespaceOnly; | 401 | var emptyRule = canBeEmpty ? EmptyRule.CanBeEmpty : EmptyRule.CanBeWhitespaceOnly; |
416 | var value = this.GetAttributeValue(sourceLineNumbers, attribute, emptyRule); | 402 | var value = this.GetAttributeValue(sourceLineNumbers, attribute, emptyRule); |
417 | 403 | ||
418 | if (String.IsNullOrEmpty(value) && canBeEmpty) | 404 | if (String.IsNullOrEmpty(value)) |
419 | { | ||
420 | return String.Empty; | ||
421 | } | ||
422 | else if (!String.IsNullOrEmpty(value)) | ||
423 | { | 405 | { |
424 | // If the value starts and ends with braces or parenthesis, accept that and strip them off. | 406 | if (canBeEmpty) |
425 | if ((value.StartsWith("{", StringComparison.Ordinal) && value.EndsWith("}", StringComparison.Ordinal)) | ||
426 | || (value.StartsWith("(", StringComparison.Ordinal) && value.EndsWith(")", StringComparison.Ordinal))) | ||
427 | { | 407 | { |
428 | value = value.Substring(1, value.Length - 2); | 408 | return String.Empty; |
429 | } | 409 | } |
430 | 410 | } | |
431 | if (generatable && "*".Equals(value, StringComparison.Ordinal)) | 411 | else |
412 | { | ||
413 | if (generatable && value == "*") | ||
432 | { | 414 | { |
433 | return value; | 415 | return value; |
434 | } | 416 | } |
435 | 417 | ||
436 | if (ParseHelper.PutGuidHere.IsMatch(value)) | 418 | if (Guid.TryParse(value, out var guid)) |
437 | { | 419 | { |
438 | this.Messaging.Write(ErrorMessages.ExampleGuid(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | 420 | return guid.ToString("B").ToUpperInvariant(); |
439 | return CompilerConstants.IllegalGuid; | ||
440 | } | 421 | } |
441 | else if (value.StartsWith("!(loc", StringComparison.Ordinal) || value.StartsWith("$(loc", StringComparison.Ordinal) || value.StartsWith("!(wix", StringComparison.Ordinal)) | 422 | |
423 | if (value.StartsWith("!(loc", StringComparison.Ordinal) || value.StartsWith("$(loc", StringComparison.Ordinal) || value.StartsWith("!(wix", StringComparison.Ordinal)) | ||
442 | { | 424 | { |
443 | return value; | 425 | return value; |
444 | } | 426 | } |
445 | else if (Guid.TryParse(value, out var guid)) | ||
446 | { | ||
447 | var uppercaseGuid = guid.ToString().ToUpperInvariant(); | ||
448 | 427 | ||
449 | // TODO: This used to be a pedantic error, what should it be now? | 428 | if (value.StartsWith("PUT-GUID-", StringComparison.OrdinalIgnoreCase) || |
450 | //if (uppercaseGuid != value) | 429 | value.StartsWith("{PUT-GUID-", StringComparison.OrdinalIgnoreCase)) |
451 | //{ | 430 | { |
452 | // this.Messaging.Write(WixErrors.GuidContainsLowercaseLetters(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | 431 | this.Messaging.Write(ErrorMessages.ExampleGuid(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); |
453 | //} | ||
454 | |||
455 | return String.Concat("{", uppercaseGuid, "}"); | ||
456 | } | 432 | } |
457 | else | 433 | else |
458 | { | 434 | { |
@@ -468,19 +444,45 @@ namespace WixToolset.Core.ExtensibilityServices | |||
468 | var access = AccessModifier.Public; | 444 | var access = AccessModifier.Public; |
469 | var value = Common.GetAttributeValue(this.Messaging, sourceLineNumbers, attribute, EmptyRule.CanBeEmpty); | 445 | var value = Common.GetAttributeValue(this.Messaging, sourceLineNumbers, attribute, EmptyRule.CanBeEmpty); |
470 | 446 | ||
471 | var match = ParseHelper.LegalIdentifierWithAccess.Match(value); | 447 | var separator = value.IndexOf(' '); |
472 | if (!match.Success) | 448 | if (separator > 0) |
473 | { | 449 | { |
474 | return null; | 450 | var prefix = value.Substring(0, separator); |
451 | switch (prefix) | ||
452 | { | ||
453 | case "public": | ||
454 | case "package": | ||
455 | access = AccessModifier.Public; | ||
456 | break; | ||
457 | |||
458 | case "internal": | ||
459 | case "library": | ||
460 | access = AccessModifier.Internal; | ||
461 | break; | ||
462 | |||
463 | case "protected": | ||
464 | case "file": | ||
465 | access = AccessModifier.Protected; | ||
466 | break; | ||
467 | |||
468 | case "private": | ||
469 | case "fragment": | ||
470 | access = AccessModifier.Private; | ||
471 | break; | ||
472 | |||
473 | default: | ||
474 | return null; | ||
475 | } | ||
476 | |||
477 | value = value.Substring(separator + 1).Trim(); | ||
475 | } | 478 | } |
476 | else if (match.Groups["access"].Success) | 479 | |
480 | if (!Common.IsIdentifier(value)) | ||
477 | { | 481 | { |
478 | access = (AccessModifier)Enum.Parse(typeof(AccessModifier), match.Groups["access"].Value, true); | 482 | this.Messaging.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); |
483 | return null; | ||
479 | } | 484 | } |
480 | 485 | else if (72 < value.Length) | |
481 | value = match.Groups["id"].Value; | ||
482 | |||
483 | if (Common.IsIdentifier(value) && 72 < value.Length) | ||
484 | { | 486 | { |
485 | this.Messaging.Write(WarningMessages.IdentifierTooLong(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | 487 | this.Messaging.Write(WarningMessages.IdentifierTooLong(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); |
486 | } | 488 | } |
@@ -520,7 +522,7 @@ namespace WixToolset.Core.ExtensibilityServices | |||
520 | } | 522 | } |
521 | else if (resultUsedToCreateReference && 1 == result.Length) | 523 | else if (resultUsedToCreateReference && 1 == result.Length) |
522 | { | 524 | { |
523 | if (value.EndsWith("\\")) | 525 | if (value.EndsWith("\\", StringComparison.Ordinal)) |
524 | { | 526 | { |
525 | if (!this.IsValidLongFilename(result[0], false, false)) | 527 | if (!this.IsValidLongFilename(result[0], false, false)) |
526 | { | 528 | { |
@@ -547,7 +549,7 @@ namespace WixToolset.Core.ExtensibilityServices | |||
547 | } | 549 | } |
548 | } | 550 | } |
549 | 551 | ||
550 | if (1 < result.Length && !value.EndsWith("\\")) | 552 | if (1 < result.Length && !value.EndsWith("\\", StringComparison.Ordinal)) |
551 | { | 553 | { |
552 | this.Messaging.Write(WarningMessages.BackslashTerminateInlineDirectorySyntax(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | 554 | this.Messaging.Write(WarningMessages.BackslashTerminateInlineDirectorySyntax(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); |
553 | } | 555 | } |
@@ -776,14 +778,7 @@ namespace WixToolset.Core.ExtensibilityServices | |||
776 | 778 | ||
777 | public bool IsValidLocIdentifier(string identifier) | 779 | public bool IsValidLocIdentifier(string identifier) |
778 | { | 780 | { |
779 | if (String.IsNullOrEmpty(identifier)) | 781 | return Common.TryParseWixVariable(identifier, 0, out var parsed) && parsed.Index == 0 && parsed.Length == identifier.Length && parsed.Namespace == "loc"; |
780 | { | ||
781 | return false; | ||
782 | } | ||
783 | |||
784 | var match = Common.WixVariableRegex.Match(identifier); | ||
785 | |||
786 | return (match.Success && "loc" == match.Groups["namespace"].Value && 0 == match.Index && identifier.Length == match.Length); | ||
787 | } | 782 | } |
788 | 783 | ||
789 | public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) | 784 | public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) |
@@ -792,29 +787,38 @@ namespace WixToolset.Core.ExtensibilityServices | |||
792 | { | 787 | { |
793 | return false; | 788 | return false; |
794 | } | 789 | } |
790 | else if (filename.Length > 259) | ||
791 | { | ||
792 | return false; | ||
793 | } | ||
795 | 794 | ||
796 | // Check for a non-period character (all periods is not legal) | 795 | // Check for a non-period character (all periods is not legal) |
797 | var nonPeriodFound = false; | 796 | var allPeriods = true; |
798 | foreach (var character in filename) | 797 | foreach (var character in filename) |
799 | { | 798 | { |
800 | if ('.' != character) | 799 | if ('.' != character) |
801 | { | 800 | { |
802 | nonPeriodFound = true; | 801 | allPeriods = false; |
803 | break; | 802 | break; |
804 | } | 803 | } |
805 | } | 804 | } |
806 | 805 | ||
806 | if (allPeriods) | ||
807 | { | ||
808 | return false; | ||
809 | } | ||
810 | |||
807 | if (allowWildcards) | 811 | if (allowWildcards) |
808 | { | 812 | { |
809 | return (nonPeriodFound && ParseHelper.LegalWildcardLongFilename.IsMatch(filename)); | 813 | return filename.IndexOfAny(Common.IllegalWildcardLongFilenameCharacters) == -1; |
810 | } | 814 | } |
811 | else if (allowRelative) | 815 | else if (allowRelative) |
812 | { | 816 | { |
813 | return (nonPeriodFound && ParseHelper.LegalRelativeLongFilename.IsMatch(filename)); | 817 | return filename.IndexOfAny(Common.IllegalRelativeLongFilenameCharacters) == -1; |
814 | } | 818 | } |
815 | else | 819 | else |
816 | { | 820 | { |
817 | return (nonPeriodFound && ParseHelper.LegalLongFilename.IsMatch(filename)); | 821 | return filename.IndexOfAny(Common.IllegalLongFilenameCharacters) == -1; |
818 | } | 822 | } |
819 | } | 823 | } |
820 | 824 | ||
diff --git a/src/WixToolset.Core/ParsedWixVariable.cs b/src/WixToolset.Core/ParsedWixVariable.cs new file mode 100644 index 00000000..9d308b77 --- /dev/null +++ b/src/WixToolset.Core/ParsedWixVariable.cs | |||
@@ -0,0 +1,19 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Core | ||
4 | { | ||
5 | internal class ParsedWixVariable | ||
6 | { | ||
7 | public int Index { get; set; } | ||
8 | |||
9 | public int Length { get; set; } | ||
10 | |||
11 | public string Namespace { get; set; } | ||
12 | |||
13 | public string Name { get; set; } | ||
14 | |||
15 | public string Scope { get; set; } | ||
16 | |||
17 | public string DefaultValue { get; set; } | ||
18 | } | ||
19 | } | ||
diff --git a/src/WixToolset.Core/VariableResolver.cs b/src/WixToolset.Core/VariableResolver.cs index 88067673..140e7def 100644 --- a/src/WixToolset.Core/VariableResolver.cs +++ b/src/WixToolset.Core/VariableResolver.cs | |||
@@ -79,150 +79,108 @@ namespace WixToolset.Core | |||
79 | 79 | ||
80 | public IVariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool errorOnUnknown) | 80 | public IVariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool errorOnUnknown) |
81 | { | 81 | { |
82 | var matches = Common.WixVariableRegex.Matches(value); | 82 | var start = 0; |
83 | var defaulted = true; | ||
84 | var delayed = false; | ||
85 | var updated = false; | ||
83 | 86 | ||
84 | // the value is the default unless it's substituted further down | 87 | while (Common.TryParseWixVariable(value, start, out var parsed)) |
85 | var result = this.ServiceProvider.GetService<IVariableResolution>(); | ||
86 | result.IsDefault = true; | ||
87 | result.Value = value; | ||
88 | |||
89 | var finalizeEscapes = false; | ||
90 | |||
91 | while (matches.Count > 0) | ||
92 | { | 88 | { |
93 | var updatedResultThisPass = false; | 89 | var variableNamespace = parsed.Namespace; |
94 | var sb = new StringBuilder(value); | 90 | var variableId = parsed.Name; |
91 | var variableDefaultValue = parsed.DefaultValue; | ||
95 | 92 | ||
96 | // notice how this code walks backward through the list | 93 | // check for an escape sequence of !! indicating the match is not a variable expression |
97 | // because it modifies the string as we move through it | 94 | if (0 < parsed.Index && '!' == value[parsed.Index - 1]) |
98 | for (var i = matches.Count - 1; 0 <= i; i--) | ||
99 | { | 95 | { |
100 | var variableNamespace = matches[i].Groups["namespace"].Value; | 96 | var sb = new StringBuilder(value); |
101 | var variableId = matches[i].Groups["fullname"].Value; | 97 | sb.Remove(parsed.Index - 1, 1); |
102 | string variableDefaultValue = null; | 98 | value = sb.ToString(); |
103 | 99 | ||
104 | // get the default value if one was specified | 100 | updated = true; |
105 | if (matches[i].Groups["value"].Success) | 101 | start = parsed.Index + parsed.Length - 1; |
106 | { | ||
107 | variableDefaultValue = matches[i].Groups["value"].Value; | ||
108 | 102 | ||
109 | // localization variables do not support inline default values | 103 | continue; |
110 | if ("loc" == variableNamespace) | 104 | } |
111 | { | 105 | |
112 | this.Messaging.Write(ErrorMessages.IllegalInlineLocVariable(sourceLineNumbers, variableId, variableDefaultValue)); | 106 | string resolvedValue = null; |
113 | } | 107 | |
108 | if ("loc" == variableNamespace) | ||
109 | { | ||
110 | // localization variables do not support inline default values | ||
111 | if (variableDefaultValue != null) | ||
112 | { | ||
113 | this.Messaging.Write(ErrorMessages.IllegalInlineLocVariable(sourceLineNumbers, variableId, variableDefaultValue)); | ||
114 | continue; | ||
114 | } | 115 | } |
115 | 116 | ||
116 | // get the scope if one was specified | 117 | if (this.locVariables.TryGetValue(variableId, out var bindVariable)) |
117 | if (matches[i].Groups["scope"].Success) | ||
118 | { | 118 | { |
119 | if ("bind" == variableNamespace) | 119 | resolvedValue = bindVariable.Value; |
120 | { | 120 | } |
121 | variableId = matches[i].Groups["name"].Value; | 121 | } |
122 | } | 122 | else if ("wix" == variableNamespace) |
123 | { | ||
124 | if (this.wixVariables.TryGetValue(variableId, out var bindVariable)) | ||
125 | { | ||
126 | resolvedValue = bindVariable.Value ?? String.Empty; | ||
127 | defaulted = false; | ||
123 | } | 128 | } |
129 | else if (null != variableDefaultValue) // default the resolved value to the inline value if one was specified | ||
130 | { | ||
131 | resolvedValue = variableDefaultValue; | ||
132 | } | ||
133 | } | ||
124 | 134 | ||
125 | // check for an escape sequence of !! indicating the match is not a variable expression | 135 | if ("bind" == variableNamespace) |
126 | if (0 < matches[i].Index && '!' == sb[matches[i].Index - 1]) | 136 | { |
137 | // Can't resolve these yet, but keep track of where we find them so they can be resolved later with less effort. | ||
138 | delayed = true; | ||
139 | start = parsed.Index + parsed.Length - 1; | ||
140 | } | ||
141 | else | ||
142 | { | ||
143 | // insert the resolved value if it was found or display an error | ||
144 | if (null != resolvedValue) | ||
127 | { | 145 | { |
128 | if (finalizeEscapes) | 146 | if (parsed.Index == 0 && parsed.Length == value.Length) |
129 | { | 147 | { |
130 | sb.Remove(matches[i].Index - 1, 1); | 148 | value = resolvedValue; |
131 | |||
132 | result.UpdatedValue = true; | ||
133 | } | 149 | } |
134 | else | 150 | else |
135 | { | 151 | { |
136 | continue; | 152 | var sb = new StringBuilder(value); |
153 | sb.Remove(parsed.Index, parsed.Length); | ||
154 | sb.Insert(parsed.Index, resolvedValue); | ||
155 | value = sb.ToString(); | ||
137 | } | 156 | } |
157 | |||
158 | updated = true; | ||
159 | start = parsed.Index; | ||
138 | } | 160 | } |
139 | else | 161 | else |
140 | { | 162 | { |
141 | string resolvedValue = null; | 163 | if ("loc" == variableNamespace && errorOnUnknown) // unresolved loc variable |
142 | |||
143 | if ("loc" == variableNamespace) | ||
144 | { | 164 | { |
145 | // warn about deprecated syntax of $(loc.var) | 165 | this.Messaging.Write(ErrorMessages.LocalizationVariableUnknown(sourceLineNumbers, variableId)); |
146 | if ('$' == sb[matches[i].Index]) | ||
147 | { | ||
148 | this.Messaging.Write(WarningMessages.DeprecatedLocalizationVariablePrefix(sourceLineNumbers, variableId)); | ||
149 | } | ||
150 | |||
151 | if (this.locVariables.TryGetValue(variableId, out var bindVariable)) | ||
152 | { | ||
153 | resolvedValue = bindVariable.Value; | ||
154 | } | ||
155 | } | 166 | } |
156 | else if ("wix" == variableNamespace) | 167 | else if ("wix" == variableNamespace && errorOnUnknown) // unresolved wix variable |
157 | { | 168 | { |
158 | // illegal syntax of $(wix.var) | 169 | this.Messaging.Write(ErrorMessages.WixVariableUnknown(sourceLineNumbers, variableId)); |
159 | if ('$' == sb[matches[i].Index]) | ||
160 | { | ||
161 | this.Messaging.Write(ErrorMessages.IllegalWixVariablePrefix(sourceLineNumbers, variableId)); | ||
162 | } | ||
163 | else | ||
164 | { | ||
165 | if (this.wixVariables.TryGetValue(variableId, out var bindVariable)) | ||
166 | { | ||
167 | resolvedValue = bindVariable.Value ?? String.Empty; | ||
168 | result.IsDefault = false; | ||
169 | } | ||
170 | else if (null != variableDefaultValue) // default the resolved value to the inline value if one was specified | ||
171 | { | ||
172 | resolvedValue = variableDefaultValue; | ||
173 | } | ||
174 | } | ||
175 | } | 170 | } |
176 | 171 | ||
177 | if ("bind" == variableNamespace) | 172 | start = parsed.Index + parsed.Length; |
178 | { | ||
179 | // can't resolve these yet, but keep track of where we find them so they can be resolved later with less effort | ||
180 | result.DelayedResolve = true; | ||
181 | } | ||
182 | else | ||
183 | { | ||
184 | // insert the resolved value if it was found or display an error | ||
185 | if (null != resolvedValue) | ||
186 | { | ||
187 | sb.Remove(matches[i].Index, matches[i].Length); | ||
188 | sb.Insert(matches[i].Index, resolvedValue); | ||
189 | |||
190 | result.UpdatedValue = true; | ||
191 | updatedResultThisPass = true; | ||
192 | } | ||
193 | else if ("loc" == variableNamespace && errorOnUnknown) // unresolved loc variable | ||
194 | { | ||
195 | this.Messaging.Write(ErrorMessages.LocalizationVariableUnknown(sourceLineNumbers, variableId)); | ||
196 | } | ||
197 | else if ("wix" == variableNamespace && errorOnUnknown) // unresolved wix variable | ||
198 | { | ||
199 | this.Messaging.Write(ErrorMessages.WixVariableUnknown(sourceLineNumbers, variableId)); | ||
200 | } | ||
201 | } | ||
202 | } | 173 | } |
203 | } | 174 | } |
204 | |||
205 | result.Value = sb.ToString(); | ||
206 | value = result.Value; | ||
207 | |||
208 | if (finalizeEscapes) | ||
209 | { | ||
210 | // escaped references have been un-escaped, so we're done | ||
211 | break; | ||
212 | } | ||
213 | else if (updatedResultThisPass) | ||
214 | { | ||
215 | // we substituted loc strings, so make another pass to see if that brought in more loc strings | ||
216 | matches = Common.WixVariableRegex.Matches(value); | ||
217 | } | ||
218 | else | ||
219 | { | ||
220 | // make one final pass to un-escape any escaped references | ||
221 | finalizeEscapes = true; | ||
222 | } | ||
223 | } | 175 | } |
224 | 176 | ||
225 | return result; | 177 | return new VariableResolution |
178 | { | ||
179 | DelayedResolve = delayed, | ||
180 | IsDefault = defaulted, | ||
181 | UpdatedValue = updated, | ||
182 | Value = value, | ||
183 | }; | ||
226 | } | 184 | } |
227 | 185 | ||
228 | private static bool TryAddWixVariable(IDictionary<string, BindVariable> variables, BindVariable variable) | 186 | private static bool TryAddWixVariable(IDictionary<string, BindVariable> variables, BindVariable variable) |