SearchPattern.cs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. //
  2. // System.IO.SearchPattern.cs: Filename glob support.
  3. //
  4. // Author:
  5. // Dan Lewis ([email protected])
  6. //
  7. // (C) 2002
  8. //
  9. using System;
  10. namespace System.IO {
  11. // FIXME: there's a complication with this algorithm under windows.
  12. // the pattern '*.*' matches all files (i think . matches the extension),
  13. // whereas under UNIX it should only match files containing the '.' character.
  14. class SearchPattern {
  15. public SearchPattern (string pattern) : this (pattern, false) { }
  16. public SearchPattern (string pattern, bool ignore)
  17. {
  18. this.ignore = ignore;
  19. Compile (pattern);
  20. }
  21. public bool IsMatch (string text)
  22. {
  23. return Match (ops, text, 0);
  24. }
  25. // private
  26. private Op ops; // the compiled pattern
  27. private bool ignore; // ignore case
  28. private void Compile (string pattern)
  29. {
  30. if (pattern == null || pattern.IndexOfAny (InvalidChars) >= 0)
  31. throw new ArgumentException ("Invalid search pattern.");
  32. if (pattern == "*") { // common case
  33. ops = new Op (OpCode.True);
  34. return;
  35. }
  36. ops = null;
  37. int ptr = 0;
  38. Op last_op = null;
  39. while (ptr < pattern.Length) {
  40. Op op;
  41. switch (pattern [ptr]) {
  42. case '?':
  43. op = new Op (OpCode.AnyChar);
  44. ++ ptr;
  45. break;
  46. case '*':
  47. op = new Op (OpCode.AnyString);
  48. ++ ptr;
  49. break;
  50. default:
  51. op = new Op (OpCode.ExactString);
  52. int end = pattern.IndexOfAny (WildcardChars, ptr);
  53. if (end < 0)
  54. end = pattern.Length;
  55. op.Argument = pattern.Substring (ptr, end - ptr);
  56. if (ignore)
  57. op.Argument = op.Argument.ToLower ();
  58. ptr = end;
  59. break;
  60. }
  61. if (last_op == null)
  62. ops = op;
  63. else
  64. last_op.Next = op;
  65. last_op = op;
  66. }
  67. if (last_op == null)
  68. ops = new Op (OpCode.End);
  69. else
  70. last_op.Next = new Op (OpCode.End);
  71. }
  72. private bool Match (Op op, string text, int ptr)
  73. {
  74. while (op != null) {
  75. switch (op.Code) {
  76. case OpCode.True:
  77. return true;
  78. case OpCode.End:
  79. if (ptr == text.Length)
  80. return true;
  81. return false;
  82. case OpCode.ExactString:
  83. int length = op.Argument.Length;
  84. if (ptr + length > text.Length)
  85. return false;
  86. string str = text.Substring (ptr, length);
  87. if (ignore)
  88. str = str.ToLower ();
  89. if (str != op.Argument)
  90. return false;
  91. ptr += length;
  92. break;
  93. case OpCode.AnyChar:
  94. if (++ ptr > text.Length)
  95. return false;
  96. break;
  97. case OpCode.AnyString:
  98. while (ptr <= text.Length) {
  99. if (Match (op.Next, text, ptr))
  100. return true;
  101. ++ ptr;
  102. }
  103. return false;
  104. }
  105. op = op.Next;
  106. }
  107. return true;
  108. }
  109. // private static
  110. private static readonly char [] WildcardChars = { '*', '?' };
  111. private static readonly char [] InvalidChars = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
  112. private class Op {
  113. public Op (OpCode code)
  114. {
  115. this.Code = code;
  116. this.Argument = null;
  117. this.Next = null;
  118. }
  119. public OpCode Code;
  120. public string Argument;
  121. public Op Next;
  122. }
  123. private enum OpCode {
  124. ExactString, // literal
  125. AnyChar, // ?
  126. AnyString, // *
  127. End, // end of pattern
  128. True // always succeeds
  129. };
  130. }
  131. }