gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // Reference: https://github.com/gobwas/glob/blob/master/glob.go
  2. //
  3. // Compile creates Glob for given pattern and strings (if any present after pattern) as separators.
  4. // The pattern syntax is:
  5. //
  6. // pattern:
  7. // { term }
  8. //
  9. // term:
  10. // `*` matches any sequence of non-separator characters
  11. // `**` matches any sequence of characters
  12. // `?` matches any single non-separator character
  13. // `[` [ `!` ] { character-range } `]`
  14. // character class (must be non-empty)
  15. // `{` pattern-list `}`
  16. // pattern alternatives
  17. // c matches character c (c != `*`, `**`, `?`, `\`, `[`, `{`, `}`)
  18. // `\` c matches character c
  19. //
  20. // character-range:
  21. // c matches character c (c != `\\`, `-`, `]`)
  22. // `\` c matches character c
  23. // lo `-` hi matches character c for lo <= c <= hi
  24. //
  25. // pattern-list:
  26. // pattern { `,` pattern }
  27. // comma-separated (without spaces) patterns
  28. //
  29. class GlobCompiler {
  30. nonSeparatorChars: string;
  31. globPattern: string;
  32. regexpPattern: string;
  33. regexp: RegExp;
  34. pos: number = 0;
  35. #compileChars(): string {
  36. let result = '';
  37. if (this.globPattern[this.pos] === '!') {
  38. this.pos++;
  39. result += '^';
  40. }
  41. while (this.pos < this.globPattern.length) {
  42. const c = this.globPattern[this.pos];
  43. this.pos++;
  44. if (c === ']') {
  45. return `[${result}]`;
  46. }
  47. if (c === '\\') {
  48. if (this.pos >= this.globPattern.length) {
  49. throw new Error('Unterminated character class escape');
  50. }
  51. this.pos++;
  52. result += `\\${this.globPattern[this.pos]}`;
  53. } else {
  54. result += c;
  55. }
  56. }
  57. throw new Error('Unterminated character class');
  58. }
  59. #compile(subPattern: boolean = false): string {
  60. let result = '';
  61. while (this.pos < this.globPattern.length) {
  62. const c = this.globPattern[this.pos];
  63. this.pos++;
  64. if (subPattern && c === '}') {
  65. return `(${result})`;
  66. }
  67. switch (c) {
  68. case '*':
  69. if (this.globPattern[this.pos] !== '*') {
  70. result += `${this.nonSeparatorChars}*`; // match any sequence of non-separator characters
  71. } else {
  72. this.pos++;
  73. result += '.*'; // match any sequence of characters
  74. }
  75. break;
  76. case '?':
  77. result += this.nonSeparatorChars; // match any single non-separator character
  78. break;
  79. case '[':
  80. result += this.#compileChars();
  81. break;
  82. case '{':
  83. result += this.#compile(true);
  84. break;
  85. case ',':
  86. result += subPattern ? '|' : ',';
  87. break;
  88. case '\\':
  89. if (this.pos >= this.globPattern.length) {
  90. throw new Error('No character to escape');
  91. }
  92. result += `\\${this.globPattern[this.pos]}`;
  93. this.pos++;
  94. break;
  95. case '.': case '+': case '^': case '$': case '(': case ')': case '|':
  96. result += `\\${c}`; // escape regexp special characters
  97. break;
  98. default:
  99. result += c;
  100. }
  101. }
  102. return result;
  103. }
  104. constructor(pattern: string, separators: string = '') {
  105. const escapedSeparators = separators.replaceAll(/[\^\]\-\\]/g, '\\$&');
  106. this.nonSeparatorChars = escapedSeparators ? `[^${escapedSeparators}]` : '.';
  107. this.globPattern = pattern;
  108. this.regexpPattern = `^${this.#compile()}$`;
  109. this.regexp = new RegExp(`^${this.regexpPattern}$`);
  110. }
  111. }
  112. export function globCompile(pattern: string, separators: string = ''): GlobCompiler {
  113. return new GlobCompiler(pattern, separators);
  114. }
  115. export function globMatch(str: string, pattern: string, separators: string = ''): boolean {
  116. try {
  117. return globCompile(pattern, separators).regexp.test(str);
  118. } catch {
  119. return false;
  120. }
  121. }