1import os
2import re
3
4
5def translate(pat, match_end=r"\Z"):
6    """Translate a shell-style pattern to a regular expression.
7
8    The pattern may include ``**<sep>`` (<sep> stands for the platform-specific path separator; "/" on POSIX systems) for
9    matching zero or more directory levels and "*" for matching zero or more arbitrary characters with the exception of
10    any path separator. Wrap meta-characters in brackets for a literal match (i.e. "[?]" to match the literal character
11    "?").
12
13    Using match_end=regex one can give a regular expression that is used to match after the regex that is generated from
14    the pattern. The default is to match the end of the string.
15
16    This function is derived from the "fnmatch" module distributed with the Python standard library.
17
18    Copyright (C) 2001-2016 Python Software Foundation. All rights reserved.
19
20    TODO: support {alt1,alt2} shell-style alternatives
21
22    """
23    sep = os.path.sep
24    n = len(pat)
25    i = 0
26    res = ""
27
28    while i < n:
29        c = pat[i]
30        i += 1
31
32        if c == "*":
33            if i + 1 < n and pat[i] == "*" and pat[i + 1] == sep:
34                # **/ == wildcard for 0+ full (relative) directory names with trailing slashes; the forward slash stands
35                # for the platform-specific path separator
36                res += r"(?:[^\%s]*\%s)*" % (sep, sep)
37                i += 2
38            else:
39                # * == wildcard for name parts (does not cross path separator)
40                res += r"[^\%s]*" % sep
41        elif c == "?":
42            # ? == any single character excluding path separator
43            res += r"[^\%s]" % sep
44        elif c == "[":
45            j = i
46            if j < n and pat[j] == "!":
47                j += 1
48            if j < n and pat[j] == "]":
49                j += 1
50            while j < n and pat[j] != "]":
51                j += 1
52            if j >= n:
53                res += "\\["
54            else:
55                stuff = pat[i:j].replace("\\", "\\\\")
56                i = j + 1
57                if stuff[0] == "!":
58                    stuff = "^" + stuff[1:]
59                elif stuff[0] == "^":
60                    stuff = "\\" + stuff
61                res += "[%s]" % stuff
62        else:
63            res += re.escape(c)
64
65    return "(?ms)" + res + match_end
66