aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/VariableResolver.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/VariableResolver.cs')
-rw-r--r--src/WixToolset.Core/VariableResolver.cs188
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)