aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Prebuild/src/Core/Parse/Preprocessor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Prebuild/src/Core/Parse/Preprocessor.cs')
-rw-r--r--Prebuild/src/Core/Parse/Preprocessor.cs663
1 files changed, 0 insertions, 663 deletions
diff --git a/Prebuild/src/Core/Parse/Preprocessor.cs b/Prebuild/src/Core/Parse/Preprocessor.cs
deleted file mode 100644
index b2306e4..0000000
--- a/Prebuild/src/Core/Parse/Preprocessor.cs
+++ /dev/null
@@ -1,663 +0,0 @@
1#region BSD License
2/*
3Copyright (c) 2004-2005 Matthew Holmes (matthew@wildfiregames.com), Dan Moorehead (dan05a@gmail.com)
4
5Redistribution and use in source and binary forms, with or without modification, are permitted
6provided that the following conditions are met:
7
8* Redistributions of source code must retain the above copyright notice, this list of conditions
9 and the following disclaimer.
10* Redistributions in binary form must reproduce the above copyright notice, this list of conditions
11 and the following disclaimer in the documentation and/or other materials provided with the
12 distribution.
13* The name of the author may not be used to endorse or promote products derived from this software
14 without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
17BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
22IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23*/
24#endregion
25
26using System;
27using System.Collections;
28using System.IO;
29using System.Text.RegularExpressions;
30using System.Xml;
31
32namespace Prebuild.Core.Parse
33{
34 /// <summary>
35 ///
36 /// </summary>
37 public enum OperatorSymbol
38 {
39 /// <summary>
40 ///
41 /// </summary>
42 None,
43 /// <summary>
44 ///
45 /// </summary>
46 Equal,
47 /// <summary>
48 ///
49 /// </summary>
50 NotEqual,
51 /// <summary>
52 ///
53 /// </summary>
54 LessThan,
55 /// <summary>
56 ///
57 /// </summary>
58 GreaterThan,
59 /// <summary>
60 ///
61 /// </summary>
62 LessThanEqual,
63 /// <summary>
64 ///
65 /// </summary>
66 GreaterThanEqual
67 }
68
69 /// <summary>
70 ///
71 /// </summary>
72 public class Preprocessor
73 {
74 #region Constants
75
76 /// <summary>
77 /// Includes the regex to look for file tags in the <?include
78 /// ?> processing instruction.
79 /// </summary>
80 private static readonly Regex includeFileRegex = new Regex("file=\"(.+?)\"");
81
82 #endregion
83
84 #region Fields
85
86 XmlDocument m_OutDoc;
87 Stack m_IfStack;
88 Hashtable m_Variables;
89
90 #endregion
91
92 #region Constructors
93
94 /// <summary>
95 /// Initializes a new instance of the <see cref="Preprocessor"/> class.
96 /// </summary>
97 public Preprocessor()
98 {
99 m_OutDoc = new XmlDocument();
100 m_IfStack = new Stack();
101 m_Variables = new Hashtable();
102
103 RegisterVariable("OS", GetOS());
104 RegisterVariable("RuntimeVersion", Environment.Version.Major);
105 RegisterVariable("RuntimeMajor", Environment.Version.Major);
106 RegisterVariable("RuntimeMinor", Environment.Version.Minor);
107 RegisterVariable("RuntimeRevision", Environment.Version.Revision);
108 }
109
110 #endregion
111
112 #region Properties
113
114 /// <summary>
115 /// Gets the processed doc.
116 /// </summary>
117 /// <value>The processed doc.</value>
118 public XmlDocument ProcessedDoc
119 {
120 get
121 {
122 return m_OutDoc;
123 }
124 }
125
126 #endregion
127
128 #region Private Methods
129
130 /// <summary>
131 /// Parts of this code were taken from NAnt and is subject to the GPL
132 /// as per NAnt's license. Thanks to the NAnt guys for this little gem.
133 /// </summary>
134 /// <returns></returns>
135 public static string GetOS()
136 {
137 PlatformID platId = Environment.OSVersion.Platform;
138 if(platId == PlatformID.Win32NT || platId == PlatformID.Win32Windows)
139 {
140 return "Win32";
141 }
142
143 if (File.Exists("/System/Library/Frameworks/Cocoa.framework/Cocoa"))
144 {
145 return "MACOSX";
146 }
147
148 /*
149 * .NET 1.x, under Mono, the UNIX code is 128. Under
150 * .NET 2.x, Mono or MS, the UNIX code is 4
151 */
152 if(Environment.Version.Major == 1)
153 {
154 if((int)platId == 128)
155 {
156 return "UNIX";
157 }
158 }
159 else if((int)platId == 4)
160 {
161 return "UNIX";
162 }
163
164 return "Unknown";
165 }
166
167 private static bool CompareNum(OperatorSymbol oper, int val1, int val2)
168 {
169 switch(oper)
170 {
171 case OperatorSymbol.Equal:
172 return (val1 == val2);
173 case OperatorSymbol.NotEqual:
174 return (val1 != val2);
175 case OperatorSymbol.LessThan:
176 return (val1 < val2);
177 case OperatorSymbol.LessThanEqual:
178 return (val1 <= val2);
179 case OperatorSymbol.GreaterThan:
180 return (val1 > val2);
181 case OperatorSymbol.GreaterThanEqual:
182 return (val1 >= val2);
183 }
184
185 throw new WarningException("Unknown operator type");
186 }
187
188 private static bool CompareStr(OperatorSymbol oper, string val1, string val2)
189 {
190 switch(oper)
191 {
192 case OperatorSymbol.Equal:
193 return (val1 == val2);
194 case OperatorSymbol.NotEqual:
195 return (val1 != val2);
196 case OperatorSymbol.LessThan:
197 return (val1.CompareTo(val2) < 0);
198 case OperatorSymbol.LessThanEqual:
199 return (val1.CompareTo(val2) <= 0);
200 case OperatorSymbol.GreaterThan:
201 return (val1.CompareTo(val2) > 0);
202 case OperatorSymbol.GreaterThanEqual:
203 return (val1.CompareTo(val2) >= 0);
204 }
205
206 throw new WarningException("Unknown operator type");
207 }
208
209 private static char NextChar(int idx, string str)
210 {
211 if((idx + 1) >= str.Length)
212 {
213 return Char.MaxValue;
214 }
215
216 return str[idx + 1];
217 }
218 // Very very simple expression parser. Can only match expressions of the form
219 // <var> <op> <value>:
220 // OS = Windows
221 // OS != Linux
222 // RuntimeMinor > 0
223 private bool ParseExpression(string exp)
224 {
225 if(exp == null)
226 {
227 throw new ArgumentException("Invalid expression, cannot be null");
228 }
229
230 exp = exp.Trim();
231 if(exp.Length < 1)
232 {
233 throw new ArgumentException("Invalid expression, cannot be 0 length");
234 }
235
236 string id = "";
237 string str = "";
238 OperatorSymbol oper = OperatorSymbol.None;
239 bool inStr = false;
240 char c;
241
242 for(int i = 0; i < exp.Length; i++)
243 {
244 c = exp[i];
245 if(Char.IsWhiteSpace(c))
246 {
247 continue;
248 }
249
250 if(Char.IsLetterOrDigit(c) || c == '_')
251 {
252 if(inStr)
253 {
254 str += c;
255 }
256 else
257 {
258 id += c;
259 }
260 }
261 else if(c == '\"')
262 {
263 inStr = !inStr;
264 if(inStr)
265 {
266 str = "";
267 }
268 }
269 else
270 {
271 if(inStr)
272 {
273 str += c;
274 }
275 else
276 {
277 switch(c)
278 {
279 case '=':
280 oper = OperatorSymbol.Equal;
281 break;
282
283 case '!':
284 if(NextChar(i, exp) == '=')
285 {
286 oper = OperatorSymbol.NotEqual;
287 }
288
289 break;
290
291 case '<':
292 if(NextChar(i, exp) == '=')
293 {
294 oper = OperatorSymbol.LessThanEqual;
295 }
296 else
297 {
298 oper = OperatorSymbol.LessThan;
299 }
300
301 break;
302
303 case '>':
304 if(NextChar(i, exp) == '=')
305 {
306 oper = OperatorSymbol.GreaterThanEqual;
307 }
308 else
309 {
310 oper = OperatorSymbol.GreaterThan;
311 }
312
313 break;
314 }
315 }
316 }
317 }
318
319
320 if(inStr)
321 {
322 throw new WarningException("Expected end of string in expression");
323 }
324
325 if(oper == OperatorSymbol.None)
326 {
327 throw new WarningException("Expected operator in expression");
328 }
329 else if(id.Length < 1)
330 {
331 throw new WarningException("Expected identifier in expression");
332 }
333 else if(str.Length < 1)
334 {
335 throw new WarningException("Expected value in expression");
336 }
337
338 bool ret = false;
339 try
340 {
341 object val = m_Variables[id.ToLower()];
342 if(val == null)
343 {
344 throw new WarningException("Unknown identifier '{0}'", id);
345 }
346
347 int numVal, numVal2;
348 string strVal, strVal2;
349 Type t = val.GetType();
350 if(t.IsAssignableFrom(typeof(int)))
351 {
352 numVal = (int)val;
353 numVal2 = Int32.Parse(str);
354 ret = CompareNum(oper, numVal, numVal2);
355 }
356 else
357 {
358 strVal = val.ToString();
359 strVal2 = str;
360 ret = CompareStr(oper, strVal, strVal2);
361 }
362 }
363 catch(ArgumentException ex)
364 {
365 ex.ToString();
366 throw new WarningException("Invalid value type for system variable '{0}', expected int", id);
367 }
368
369 return ret;
370 }
371
372 #endregion
373
374 #region Public Methods
375
376 /// <summary>
377 ///
378 /// </summary>
379 /// <param name="name"></param>
380 /// <param name="variableValue"></param>
381 public void RegisterVariable(string name, object variableValue)
382 {
383 if(name == null || variableValue == null)
384 {
385 return;
386 }
387
388 m_Variables[name.ToLower()] = variableValue;
389 }
390
391 /// <summary>
392 /// Performs validation on the xml source as well as evaluates conditional and flow expresions
393 /// </summary>
394 /// <exception cref="ArgumentException">For invalid use of conditional expressions or for invalid XML syntax. If a XmlValidatingReader is passed, then will also throw exceptions for non-schema-conforming xml</exception>
395 /// <param name="reader"></param>
396 /// <returns>the output xml </returns>
397 public string Process(XmlReader initialReader)
398 {
399 if(initialReader == null)
400 {
401 throw new ArgumentException("Invalid XML reader to pre-process");
402 }
403
404 IfContext context = new IfContext(true, true, IfState.None);
405 StringWriter xmlText = new StringWriter();
406 XmlTextWriter writer = new XmlTextWriter(xmlText);
407 writer.Formatting = Formatting.Indented;
408
409 // Create a queue of XML readers and add the initial
410 // reader to it. Then we process until we run out of
411 // readers which lets the <?include?> operation add more
412 // readers to generate a multi-file parser and not require
413 // XML fragments that a recursive version would use.
414 Stack readerStack = new Stack();
415 readerStack.Push(initialReader);
416
417 while(readerStack.Count > 0)
418 {
419 // Pop off the next reader.
420 XmlReader reader = (XmlReader) readerStack.Pop();
421
422 // Process through this XML reader until it is
423 // completed (or it is replaced by the include
424 // operation).
425 while(reader.Read())
426 {
427 // The prebuild file has a series of processing
428 // instructions which allow for specific
429 // inclusions based on operating system or to
430 // include additional files.
431 if(reader.NodeType == XmlNodeType.ProcessingInstruction)
432 {
433 bool ignore = false;
434
435 switch(reader.LocalName)
436 {
437 case "include":
438 // use regular expressions to parse out the attributes.
439 MatchCollection matches = includeFileRegex.Matches(reader.Value);
440
441 // make sure there is only one file attribute.
442 if(matches.Count > 1)
443 {
444 throw new WarningException("An <?include ?> node was found, but it specified more than one file.");
445 }
446
447 if(matches.Count == 0)
448 {
449 throw new WarningException("An <?include ?> node was found, but it did not specify the file attribute.");
450 }
451
452 // Push current reader back onto the stack.
453 readerStack.Push(reader);
454
455 // Pull the file out from the regex and make sure it is a valid file before using it.
456 string filename = matches[0].Groups[1].Value;
457
458 filename = String.Join(Path.DirectorySeparatorChar.ToString(), filename.Split(new char[] { '/', '\\' }));
459
460 if (!filename.Contains("*"))
461 {
462 FileInfo includeFile = new FileInfo(filename);
463
464 if (!includeFile.Exists)
465 {
466 throw new WarningException("Cannot include file: " + includeFile.FullName);
467 }
468
469 // Create a new reader object for this file, and push it onto the stack
470 XmlReader newReader = new XmlTextReader(includeFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read));
471 readerStack.Push(newReader);
472 }
473 else
474 {
475 WildCardInclude(readerStack, filename);
476 }
477
478 // continue reading with whatever reader is on the top of the stack
479 reader = (XmlReader)readerStack.Pop();
480 ignore = true;
481
482 break;
483
484 case "if":
485 m_IfStack.Push(context);
486 context = new IfContext(context.Keep & context.Active, ParseExpression(reader.Value), IfState.If);
487 ignore = true;
488 break;
489
490 case "elseif":
491 if(m_IfStack.Count == 0)
492 {
493 throw new WarningException("Unexpected 'elseif' outside of 'if'");
494 }
495 else if(context.State != IfState.If && context.State != IfState.ElseIf)
496 {
497 throw new WarningException("Unexpected 'elseif' outside of 'if'");
498 }
499
500 context.State = IfState.ElseIf;
501 if(!context.EverKept)
502 {
503 context.Keep = ParseExpression(reader.Value);
504 }
505 else
506 {
507 context.Keep = false;
508 }
509
510 ignore = true;
511 break;
512
513 case "else":
514 if(m_IfStack.Count == 0)
515 {
516 throw new WarningException("Unexpected 'else' outside of 'if'");
517 }
518 else if(context.State != IfState.If && context.State != IfState.ElseIf)
519 {
520 throw new WarningException("Unexpected 'else' outside of 'if'");
521 }
522
523 context.State = IfState.Else;
524 context.Keep = !context.EverKept;
525 ignore = true;
526 break;
527
528 case "endif":
529 if(m_IfStack.Count == 0)
530 {
531 throw new WarningException("Unexpected 'endif' outside of 'if'");
532 }
533
534 context = (IfContext)m_IfStack.Pop();
535 ignore = true;
536 break;
537 }
538
539 if(ignore)
540 {
541 continue;
542 }
543 }//end pre-proc instruction
544
545 if(!context.Active || !context.Keep)
546 {
547 continue;
548 }
549
550 switch(reader.NodeType)
551 {
552 case XmlNodeType.Element:
553 bool empty = reader.IsEmptyElement;
554 writer.WriteStartElement(reader.Name);
555
556 while (reader.MoveToNextAttribute())
557 {
558 writer.WriteAttributeString(reader.Name, reader.Value);
559 }
560
561 if(empty)
562 {
563 writer.WriteEndElement();
564 }
565
566 break;
567
568 case XmlNodeType.EndElement:
569 writer.WriteEndElement();
570 break;
571
572 case XmlNodeType.Text:
573 writer.WriteString(reader.Value);
574 break;
575
576 case XmlNodeType.CDATA:
577 writer.WriteCData(reader.Value);
578 break;
579
580 default:
581 break;
582 }
583 }
584
585 if(m_IfStack.Count != 0)
586 {
587 throw new WarningException("Mismatched 'if', 'endif' pair");
588 }
589 }
590
591 return xmlText.ToString();
592 }
593
594 private static void WildCardInclude(Stack readerStack, string include)
595 {
596 if (!include.Contains("*"))
597 {
598 return;
599 }
600
601// Console.WriteLine("Processing {0}", include);
602
603 // Break up the include into pre and post wildcard sections
604 string preWildcard = include.Substring(0, include.IndexOf("*"));
605 string postWildcard = include.Substring(include.IndexOf("*") + 2);
606
607 // If preWildcard is a directory, recurse
608 if (Directory.Exists(preWildcard))
609 {
610 string[] directories = Directory.GetDirectories(preWildcard);
611 Array.Sort(directories);
612 Array.Reverse(directories);
613 foreach (string dirPath in directories )
614 {
615 Console.WriteLine("Scanning : {0}", dirPath);
616
617 string includeFile = Path.Combine(dirPath, postWildcard);
618 if (includeFile.Contains("*"))
619 {
620 // postWildcard included another wildcard, recurse.
621 WildCardInclude(readerStack, includeFile);
622 }
623 else
624 {
625 FileInfo file = new FileInfo(includeFile);
626 if (file.Exists)
627 {
628 Console.WriteLine("Including File: {0}", includeFile);
629 XmlReader newReader = new XmlTextReader(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read));
630 readerStack.Push(newReader);
631 }
632 }
633 }
634 }
635 else
636 {
637 // preWildcard is not a path to a directory, so the wildcard is in the filename
638 string searchFilename = Path.GetFileName(preWildcard.Substring(preWildcard.IndexOf("/") + 1) + "*" + postWildcard);
639 // Console.WriteLine("searchFilename: {0}", searchFilename);
640
641 string searchDirectory = Path.GetDirectoryName(preWildcard);
642 // Console.WriteLine("searchDirectory: {0}", searchDirectory);
643
644 string[] files = Directory.GetFiles(searchDirectory, searchFilename);
645 Array.Sort(files);
646 Array.Reverse(files);
647 foreach (string includeFile in files)
648 {
649 FileInfo file = new FileInfo(includeFile);
650 if (file.Exists)
651 {
652 Console.WriteLine("Including File: {0}", includeFile);
653 XmlReader newReader = new XmlTextReader(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read));
654 readerStack.Push(newReader);
655 }
656 }
657 }
658
659 }
660
661 #endregion
662 }
663}