Translate a shell PATTERN to a regular expression. There is no way to quote meta-characters.
(pat)
| 61 | |
| 62 | |
| 63 | def translate(pat): |
| 64 | """Translate a shell PATTERN to a regular expression. |
| 65 | |
| 66 | There is no way to quote meta-characters. |
| 67 | """ |
| 68 | i, n = 0, len(pat) |
| 69 | res = '^' |
| 70 | while i < n: |
| 71 | c = pat[i] |
| 72 | i = i + 1 |
| 73 | if c == '*': |
| 74 | if i < n and pat[i] == '*': |
| 75 | # is some flavor of "**" |
| 76 | i = i + 1 |
| 77 | # Treat **/ as ** so eat the "/" |
| 78 | if i < n and pat[i] == '/': |
| 79 | i = i + 1 |
| 80 | if i >= n: |
| 81 | # is "**EOF" - to align with .gitignore just accept all |
| 82 | res = f"{res}.*" |
| 83 | else: |
| 84 | # is "**" |
| 85 | # Note that this allows for any # of /'s (even 0) because |
| 86 | # the .* will eat everything, even /'s |
| 87 | res = f"{res}(.*/)?" |
| 88 | else: |
| 89 | # is "*" so map it to anything but "/" |
| 90 | res = f"{res}[^/]*" |
| 91 | elif c == '?': |
| 92 | # "?" is any char except "/" |
| 93 | res = f"{res}[^/]" |
| 94 | elif c == '[': |
| 95 | j = i |
| 96 | if j < n and pat[j] == '!': |
| 97 | j = j + 1 |
| 98 | if j < n and pat[j] == ']': |
| 99 | j = j + 1 |
| 100 | while j < n and pat[j] != ']': |
| 101 | j = j + 1 |
| 102 | if j >= n: |
| 103 | res = f"{res}\\[" |
| 104 | else: |
| 105 | stuff = pat[i:j].replace('\\', '\\\\') |
| 106 | i = j + 1 |
| 107 | if stuff[0] == '!': |
| 108 | stuff = f"^{stuff[1:]}" |
| 109 | elif stuff[0] == '^': |
| 110 | stuff = f"\\{stuff}" |
| 111 | res = f'{res}[{stuff}]' |
| 112 | else: |
| 113 | res = res + re.escape(c) |
| 114 | |
| 115 | return f"{res}$" |