Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

246 lines
7.3 KiB

  1. /*****************************************************************************
  2. *
  3. * match.cpp
  4. *
  5. * Highly-specialized depot path matching class
  6. *
  7. *****************************************************************************/
  8. #include "sdview.h"
  9. Match::Match(LPCTSTR pszPattern)
  10. {
  11. Tokenizer tok(pszPattern);
  12. String str, strPath, strPats;
  13. while (tok.Token(str)) {
  14. if (MapToFullDepotPath(str, strPath)) {
  15. _AddPattern(strPath, strPats);
  16. }
  17. }
  18. _pszzPats = new TCHAR[strPats.Length()];
  19. if (_pszzPats) {
  20. CopyMemory(_pszzPats, strPats, strPats.Length() * sizeof(TCHAR));
  21. _pszEnd = _pszzPats + strPats.Length();
  22. }
  23. }
  24. BOOL Match::Matches(LPCTSTR pszPath)
  25. {
  26. LPCTSTR pszPat;
  27. if (_pszzPats) {
  28. for (pszPat = _pszzPats;
  29. pszPat < _pszEnd; pszPat += lstrlen(pszPat) + 1) {
  30. if (_Matches(pszPat, pszPath)) {
  31. return TRUE;
  32. }
  33. }
  34. }
  35. return FALSE;
  36. }
  37. #define PAT_END ((TCHAR)0)
  38. #define PAT_START ((TCHAR)1)
  39. #define PAT_DOTS ((TCHAR)2)
  40. #define PAT_STAR ((TCHAR)3)
  41. #define PAT_BRANCH ((TCHAR)4)
  42. #define MAX_BACKTRACK 20 // totally arbitrary
  43. void Match::_AddPattern(LPTSTR pszPat, String& strPats)
  44. {
  45. CharLower(pszPat);
  46. //
  47. // "compile" the pattern by changing "..." to PAT_DOTS and
  48. // "%1" and "*" to PAT_STAR.
  49. //
  50. // Oh, and change "//depot/blah/" and "//depot/private/blah/" to
  51. // "//depot/<PAT_BRANCH>" so we can track across branches.
  52. //
  53. LPTSTR pszIn, pszOut;
  54. int iWildcards = 0;
  55. int cSlashes = 0;
  56. TCHAR pat;
  57. pszIn = pszOut = pszPat;
  58. for (;;) {
  59. switch (*pszIn) {
  60. case TEXT('\r'):
  61. case TEXT('\n'):
  62. case TEXT('\0'):
  63. goto endcompile;
  64. case TEXT('.'):
  65. if (pszIn[1] == TEXT('.') && pszIn[2] == TEXT('.')) {
  66. pszIn += 3;
  67. pat = PAT_DOTS;
  68. goto L_wildcard;
  69. } else {
  70. goto L_default;
  71. }
  72. break;
  73. case TEXT('%'):
  74. if ((UINT)(pszIn[1] - TEXT('1')) < 9) {
  75. pszIn += 2;
  76. pat = PAT_STAR;
  77. goto L_wildcard;
  78. } else {
  79. goto L_default;
  80. }
  81. break;
  82. case TEXT('*'):
  83. pszIn++;
  84. pat = PAT_STAR;
  85. goto L_wildcard;
  86. L_wildcard:
  87. //
  88. // Collapse consecutive wildcards for perf. Otherwise
  89. // a search string of a****b will take exponential
  90. // time.
  91. //
  92. if (pszOut[-1] == pat) {
  93. // ** and ...... are the same as * and ... (respectively)
  94. // so just throw away the second wildcard.
  95. } else if (pszOut[-1] == (PAT_STAR + PAT_DOTS - pat)) {
  96. // ...* and *... are the same as "..."
  97. pszOut[-1] = PAT_DOTS;
  98. } else if (iWildcards++ < MAX_BACKTRACK) {
  99. // just a regular ol' wildcard
  100. *pszOut++ = pat;
  101. } else {
  102. *pszOut++ = PAT_DOTS; // Give up when the limit is reached
  103. goto endcompile;
  104. }
  105. break;
  106. case TEXT('/'):
  107. cSlashes++;
  108. if (cSlashes == 3) {
  109. if (StringBeginsWith(pszIn, TEXT("/private/"))) {
  110. // a private branch
  111. *pszOut++ = PAT_BRANCH;
  112. pszIn += 9; // length of "/private/"
  113. } else {
  114. // a main branch
  115. *pszOut++ = PAT_BRANCH;
  116. }
  117. // Skip over the branch name
  118. while (*pszIn != TEXT('/') &&
  119. *pszIn != TEXT('\r') &&
  120. *pszIn != TEXT('\n') &&
  121. *pszIn != TEXT('\0')) {
  122. pszIn++;
  123. }
  124. } else {
  125. goto L_default;
  126. }
  127. break;
  128. L_default:
  129. default:
  130. *pszOut++ = *pszIn++;
  131. }
  132. }
  133. endcompile:;
  134. *pszOut++ = PAT_END;
  135. // Now add it to the list of patterns we care about
  136. strPats << PAT_START << Substring(pszPat, pszOut);
  137. }
  138. //
  139. // This is the fun part -- funky pattern matching.
  140. //
  141. // The pszPath is assumed to be of the form
  142. //
  143. // //depot/fully/qualified/path#n
  144. //
  145. // PAT_DOTS matches any string.
  146. // PAT_STAR matches any string not including slash.
  147. //
  148. // This code is adapted from code I wrote back in 1993 for the
  149. // Windows 95 Netware emulation layer. I've also seen it stolen
  150. // by Wininet. I guess good code never dies. Or maybe it's just
  151. // that pattern matching is hard. (I suspect the latter because
  152. // the Wininet folks stole the code and then adapted it incorrectly.)
  153. //
  154. BOOL Match::_Matches(LPCTSTR pszPat, LPCTSTR pszPath)
  155. {
  156. struct Backtrack {
  157. int iStart;
  158. int iEnd;
  159. };
  160. Backtrack rgbt[MAX_BACKTRACK+1]; /* +1 for PAT_START fake-backtrack point */
  161. Backtrack *pbt = rgbt;
  162. int i, j; /* i = index to pattern, j = index to target */
  163. int m = lstrlen(pszPath); /* m = length of target */
  164. int back; /* First available slot in backtrack array */
  165. i = -1; /* Will be advanced to 0 */
  166. j = 0;
  167. advance:
  168. ++i;
  169. switch (pszPat[i]) {
  170. case PAT_START: pbt->iEnd = 0; goto advance;
  171. case PAT_END: if (pszPath[j] == TEXT('#')) return TRUE;
  172. else goto retreat;
  173. case PAT_DOTS: pbt++; // this is a backtracking rule
  174. pbt->iStart = j;
  175. pbt->iEnd = j = m; goto advance;
  176. case PAT_STAR: pbt++; // this is a backtracking rule
  177. pbt->iStart = j;
  178. while (pszPath[j] != TEXT('/') &&
  179. pszPath[j] != TEXT('#') &&
  180. pszPath[j] != TEXT('\0')) {
  181. j++;
  182. }
  183. pbt->iEnd = j; goto advance;
  184. case PAT_BRANCH: // this is a non-backtracking rule
  185. if (pszPath[j] != TEXT('/')) goto retreat;
  186. if (StringBeginsWith(&pszPath[j], TEXT("/private/"))) {
  187. j += 8;
  188. }
  189. // Skip over the branch name
  190. do {
  191. j++;
  192. } while (pszPath[j] != TEXT('/') &&
  193. pszPath[j] != TEXT('#') &&
  194. pszPath[j] != TEXT('\0'));
  195. goto advance;
  196. default: if (pszPath[j] == pszPat[i]) {
  197. j++;
  198. goto advance;
  199. } else if (pszPath[j] >= TEXT('A') &&
  200. pszPath[j] <= TEXT('Z') &&
  201. pszPath[j] - TEXT('A') + TEXT('a') == pszPat[i]) {
  202. // I hate case-insensitivity
  203. j++;
  204. goto advance;
  205. } else goto retreat;
  206. }
  207. retreat:
  208. --i;
  209. switch (pszPat[i]) {
  210. case PAT_START: return FALSE; // cannot backtrack further
  211. case PAT_DOTS:
  212. case PAT_STAR: if (pbt->iStart == pbt->iEnd) {
  213. pbt--;
  214. goto retreat;
  215. }
  216. j = --pbt->iEnd; goto advance;
  217. default: goto retreat;
  218. }
  219. }