diff options
Diffstat (limited to 'src/WixToolset.Core/VariableResolver.cs')
-rw-r--r-- | src/WixToolset.Core/VariableResolver.cs | 188 |
1 files changed, 73 insertions, 115 deletions
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) |