We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Regression in .dockerignore parsing with trailing '**'
docker==2.5.0
docker-compose==1.15.0
docker-pycreds==0.2.1
dockerpty==0.4.1

Having a line with trailing '**' in .dockerignore is causing an IndexError when calling docker-compose. Version 2.4.2 does not cause a crash. E.g. the following line causes the crash:

subdir/**


Traceback (most recent call last):
  File "/usr/local/bin/docker-compose", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python2.7/dist-packages/compose/cli/main.py", line 68, in main
    command()
  File "/usr/local/lib/python2.7/dist-packages/compose/cli/main.py", line 118, in perform_command
    handler(command, command_options)
  File "/usr/local/lib/python2.7/dist-packages/compose/cli/main.py", line 239, in build
    build_args=build_args)
  File "/usr/local/lib/python2.7/dist-packages/compose/project.py", line 340, in build
    service.build(no_cache, pull, force_rm, build_args)
  File "/usr/local/lib/python2.7/dist-packages/compose/service.py", line 910, in build
    network_mode=build_opts.get('network', None),
  File "/usr/local/lib/python2.7/dist-packages/docker/api/build.py", line 148, in build
    path, exclude=exclude, dockerfile=dockerfile, gzip=gzip
  File "/usr/local/lib/python2.7/dist-packages/docker/utils/build.py", line 13, in tar
    files=sorted(exclude_paths(root, exclude, dockerfile=dockerfile)),
  File "/usr/local/lib/python2.7/dist-packages/docker/utils/build.py", line 38, in exclude_paths
    has_exceptions=len(exceptions) > 0)
  File "/usr/local/lib/python2.7/dist-packages/docker/utils/build.py", line 113, in get_paths
    os.path.join(parent, d), exclude_patterns, include_patterns
  File "/usr/local/lib/python2.7/dist-packages/docker/utils/build.py", line 94, in should_check_directory
    directory_path, exclude_patterns, include_patterns
  File "/usr/local/lib/python2.7/dist-packages/docker/utils/build.py", line 60, in should_include
    if match_path(path, pattern):
  File "/usr/local/lib/python2.7/dist-packages/docker/utils/build.py", line 143, in match_path
    return fnmatch('/'.join(path_components), '/'.join(pattern_components))
  File "/usr/local/lib/python2.7/dist-packages/docker/utils/fnmatch.py", line 44, in fnmatch
    return fnmatchcase(name, pat)
  File "/usr/local/lib/python2.7/dist-packages/docker/utils/fnmatch.py", line 56, in fnmatchcase
    res = translate(pat)
  File "/usr/local/lib/python2.7/dist-packages/docker/utils/fnmatch.py", line 78, in translate
    if pat[i] == '/':
IndexError: string index out of range

The check for the trailing slash in fnmatch.py (https://github.com/docker/docker-py/blob/d798afca7e866641a357275ae768a0a686d68882/docker/utils/fnmatch.py#L78)
seems to be missing boundary checks after the index has been incremented
on previous line.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `docker/utils/fnmatch.py`
Content:
```
1 """Filename matching with shell patterns.
2 
3 fnmatch(FILENAME, PATTERN) matches according to the local convention.
4 fnmatchcase(FILENAME, PATTERN) always takes case in account.
5 
6 The functions operate by translating the pattern into a regular
7 expression.  They cache the compiled regular expressions for speed.
8 
9 The function translate(PATTERN) returns a regular expression
10 corresponding to PATTERN.  (It does not compile it.)
11 """
12 
13 import re
14 
15 __all__ = ["fnmatch", "fnmatchcase", "translate"]
16 
17 _cache = {}
18 _MAXCACHE = 100
19 
20 
21 def _purge():
22     """Clear the pattern cache"""
23     _cache.clear()
24 
25 
26 def fnmatch(name, pat):
27     """Test whether FILENAME matches PATTERN.
28 
29     Patterns are Unix shell style:
30 
31     *       matches everything
32     ?       matches any single character
33     [seq]   matches any character in seq
34     [!seq]  matches any char not in seq
35 
36     An initial period in FILENAME is not special.
37     Both FILENAME and PATTERN are first case-normalized
38     if the operating system requires it.
39     If you don't want this, use fnmatchcase(FILENAME, PATTERN).
40     """
41 
42     name = name.lower()
43     pat = pat.lower()
44     return fnmatchcase(name, pat)
45 
46 
47 def fnmatchcase(name, pat):
48     """Test whether FILENAME matches PATTERN, including case.
49     This is a version of fnmatch() which doesn't case-normalize
50     its arguments.
51     """
52 
53     try:
54         re_pat = _cache[pat]
55     except KeyError:
56         res = translate(pat)
57         if len(_cache) >= _MAXCACHE:
58             _cache.clear()
59         _cache[pat] = re_pat = re.compile(res)
60     return re_pat.match(name) is not None
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 pat[i] == '/':
79                     i = i + 1
80                 if i >= n:
81                     # is "**EOF" - to align with .gitignore just accept all
82                     res = 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 = res + '(.*/)?'
88             else:
89                 # is "*" so map it to anything but "/"
90                 res = res + '[^/]*'
91         elif c == '?':
92             # "?" is any char except "/"
93             res = 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 = res + '\\['
104             else:
105                 stuff = pat[i:j].replace('\\', '\\\\')
106                 i = j + 1
107                 if stuff[0] == '!':
108                     stuff = '^' + stuff[1:]
109                 elif stuff[0] == '^':
110                     stuff = '\\' + stuff
111                 res = '%s[%s]' % (res, stuff)
112         else:
113             res = res + re.escape(c)
114     return res + '$'
115
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/docker/utils/fnmatch.py b/docker/utils/fnmatch.py
--- a/docker/utils/fnmatch.py
+++ b/docker/utils/fnmatch.py
@@ -75,7 +75,7 @@
                 # is some flavor of "**"
                 i = i + 1
                 # Treat **/ as ** so eat the "/"
-                if pat[i] == '/':
+                if i < n and pat[i] == '/':
                     i = i + 1
                 if i >= n:
                     # is "**EOF" - to align with .gitignore just accept all

{"golden_diff": "diff --git a/docker/utils/fnmatch.py b/docker/utils/fnmatch.py\n--- a/docker/utils/fnmatch.py\n+++ b/docker/utils/fnmatch.py\n@@ -75,7 +75,7 @@\n                 # is some flavor of \"**\"\n                 i = i + 1\n                 # Treat **/ as ** so eat the \"/\"\n-                if pat[i] == '/':\n+                if i < n and pat[i] == '/':\n                     i = i + 1\n                 if i >= n:\n                     # is \"**EOF\" - to align with .gitignore just accept all\n", "issue": "Regression in .dockerignore parsing with trailing '**'\ndocker==2.5.0\r\ndocker-compose==1.15.0\r\ndocker-pycreds==0.2.1\r\ndockerpty==0.4.1\r\n\r\nHaving a line with trailing '**' in .dockerignore is causing an IndexError when calling docker-compose. Version 2.4.2 does not cause a crash. E.g. the following line causes the crash:\r\n\r\nsubdir/**\r\n\r\n\r\nTraceback (most recent call last):\r\n  File \"/usr/local/bin/docker-compose\", line 11, in <module>\r\n    sys.exit(main())\r\n  File \"/usr/local/lib/python2.7/dist-packages/compose/cli/main.py\", line 68, in main\r\n    command()\r\n  File \"/usr/local/lib/python2.7/dist-packages/compose/cli/main.py\", line 118, in perform_command\r\n    handler(command, command_options)\r\n  File \"/usr/local/lib/python2.7/dist-packages/compose/cli/main.py\", line 239, in build\r\n    build_args=build_args)\r\n  File \"/usr/local/lib/python2.7/dist-packages/compose/project.py\", line 340, in build\r\n    service.build(no_cache, pull, force_rm, build_args)\r\n  File \"/usr/local/lib/python2.7/dist-packages/compose/service.py\", line 910, in build\r\n    network_mode=build_opts.get('network', None),\r\n  File \"/usr/local/lib/python2.7/dist-packages/docker/api/build.py\", line 148, in build\r\n    path, exclude=exclude, dockerfile=dockerfile, gzip=gzip\r\n  File \"/usr/local/lib/python2.7/dist-packages/docker/utils/build.py\", line 13, in tar\r\n    files=sorted(exclude_paths(root, exclude, dockerfile=dockerfile)),\r\n  File \"/usr/local/lib/python2.7/dist-packages/docker/utils/build.py\", line 38, in exclude_paths\r\n    has_exceptions=len(exceptions) > 0)\r\n  File \"/usr/local/lib/python2.7/dist-packages/docker/utils/build.py\", line 113, in get_paths\r\n    os.path.join(parent, d), exclude_patterns, include_patterns\r\n  File \"/usr/local/lib/python2.7/dist-packages/docker/utils/build.py\", line 94, in should_check_directory\r\n    directory_path, exclude_patterns, include_patterns\r\n  File \"/usr/local/lib/python2.7/dist-packages/docker/utils/build.py\", line 60, in should_include\r\n    if match_path(path, pattern):\r\n  File \"/usr/local/lib/python2.7/dist-packages/docker/utils/build.py\", line 143, in match_path\r\n    return fnmatch('/'.join(path_components), '/'.join(pattern_components))\r\n  File \"/usr/local/lib/python2.7/dist-packages/docker/utils/fnmatch.py\", line 44, in fnmatch\r\n    return fnmatchcase(name, pat)\r\n  File \"/usr/local/lib/python2.7/dist-packages/docker/utils/fnmatch.py\", line 56, in fnmatchcase\r\n    res = translate(pat)\r\n  File \"/usr/local/lib/python2.7/dist-packages/docker/utils/fnmatch.py\", line 78, in translate\r\n    if pat[i] == '/':\r\nIndexError: string index out of range\r\n\r\nThe check for the trailing slash in fnmatch.py (https://github.com/docker/docker-py/blob/d798afca7e866641a357275ae768a0a686d68882/docker/utils/fnmatch.py#L78)\r\nseems to be missing boundary checks after the index has been incremented\r\non previous line.\n", "before_files": [{"content": "\"\"\"Filename matching with shell patterns.\n\nfnmatch(FILENAME, PATTERN) matches according to the local convention.\nfnmatchcase(FILENAME, PATTERN) always takes case in account.\n\nThe functions operate by translating the pattern into a regular\nexpression.  They cache the compiled regular expressions for speed.\n\nThe function translate(PATTERN) returns a regular expression\ncorresponding to PATTERN.  (It does not compile it.)\n\"\"\"\n\nimport re\n\n__all__ = [\"fnmatch\", \"fnmatchcase\", \"translate\"]\n\n_cache = {}\n_MAXCACHE = 100\n\n\ndef _purge():\n    \"\"\"Clear the pattern cache\"\"\"\n    _cache.clear()\n\n\ndef fnmatch(name, pat):\n    \"\"\"Test whether FILENAME matches PATTERN.\n\n    Patterns are Unix shell style:\n\n    *       matches everything\n    ?       matches any single character\n    [seq]   matches any character in seq\n    [!seq]  matches any char not in seq\n\n    An initial period in FILENAME is not special.\n    Both FILENAME and PATTERN are first case-normalized\n    if the operating system requires it.\n    If you don't want this, use fnmatchcase(FILENAME, PATTERN).\n    \"\"\"\n\n    name = name.lower()\n    pat = pat.lower()\n    return fnmatchcase(name, pat)\n\n\ndef fnmatchcase(name, pat):\n    \"\"\"Test whether FILENAME matches PATTERN, including case.\n    This is a version of fnmatch() which doesn't case-normalize\n    its arguments.\n    \"\"\"\n\n    try:\n        re_pat = _cache[pat]\n    except KeyError:\n        res = translate(pat)\n        if len(_cache) >= _MAXCACHE:\n            _cache.clear()\n        _cache[pat] = re_pat = re.compile(res)\n    return re_pat.match(name) is not None\n\n\ndef translate(pat):\n    \"\"\"Translate a shell PATTERN to a regular expression.\n\n    There is no way to quote meta-characters.\n    \"\"\"\n    i, n = 0, len(pat)\n    res = '^'\n    while i < n:\n        c = pat[i]\n        i = i + 1\n        if c == '*':\n            if i < n and pat[i] == '*':\n                # is some flavor of \"**\"\n                i = i + 1\n                # Treat **/ as ** so eat the \"/\"\n                if pat[i] == '/':\n                    i = i + 1\n                if i >= n:\n                    # is \"**EOF\" - to align with .gitignore just accept all\n                    res = res + '.*'\n                else:\n                    # is \"**\"\n                    # Note that this allows for any # of /'s (even 0) because\n                    # the .* will eat everything, even /'s\n                    res = res + '(.*/)?'\n            else:\n                # is \"*\" so map it to anything but \"/\"\n                res = res + '[^/]*'\n        elif c == '?':\n            # \"?\" is any char except \"/\"\n            res = res + '[^/]'\n        elif c == '[':\n            j = i\n            if j < n and pat[j] == '!':\n                j = j + 1\n            if j < n and pat[j] == ']':\n                j = j + 1\n            while j < n and pat[j] != ']':\n                j = j + 1\n            if j >= n:\n                res = res + '\\\\['\n            else:\n                stuff = pat[i:j].replace('\\\\', '\\\\\\\\')\n                i = j + 1\n                if stuff[0] == '!':\n                    stuff = '^' + stuff[1:]\n                elif stuff[0] == '^':\n                    stuff = '\\\\' + stuff\n                res = '%s[%s]' % (res, stuff)\n        else:\n            res = res + re.escape(c)\n    return res + '$'\n", "path": "docker/utils/fnmatch.py"}], "after_files": [{"content": "\"\"\"Filename matching with shell patterns.\n\nfnmatch(FILENAME, PATTERN) matches according to the local convention.\nfnmatchcase(FILENAME, PATTERN) always takes case in account.\n\nThe functions operate by translating the pattern into a regular\nexpression.  They cache the compiled regular expressions for speed.\n\nThe function translate(PATTERN) returns a regular expression\ncorresponding to PATTERN.  (It does not compile it.)\n\"\"\"\n\nimport re\n\n__all__ = [\"fnmatch\", \"fnmatchcase\", \"translate\"]\n\n_cache = {}\n_MAXCACHE = 100\n\n\ndef _purge():\n    \"\"\"Clear the pattern cache\"\"\"\n    _cache.clear()\n\n\ndef fnmatch(name, pat):\n    \"\"\"Test whether FILENAME matches PATTERN.\n\n    Patterns are Unix shell style:\n\n    *       matches everything\n    ?       matches any single character\n    [seq]   matches any character in seq\n    [!seq]  matches any char not in seq\n\n    An initial period in FILENAME is not special.\n    Both FILENAME and PATTERN are first case-normalized\n    if the operating system requires it.\n    If you don't want this, use fnmatchcase(FILENAME, PATTERN).\n    \"\"\"\n\n    name = name.lower()\n    pat = pat.lower()\n    return fnmatchcase(name, pat)\n\n\ndef fnmatchcase(name, pat):\n    \"\"\"Test whether FILENAME matches PATTERN, including case.\n    This is a version of fnmatch() which doesn't case-normalize\n    its arguments.\n    \"\"\"\n\n    try:\n        re_pat = _cache[pat]\n    except KeyError:\n        res = translate(pat)\n        if len(_cache) >= _MAXCACHE:\n            _cache.clear()\n        _cache[pat] = re_pat = re.compile(res)\n    return re_pat.match(name) is not None\n\n\ndef translate(pat):\n    \"\"\"Translate a shell PATTERN to a regular expression.\n\n    There is no way to quote meta-characters.\n    \"\"\"\n    i, n = 0, len(pat)\n    res = '^'\n    while i < n:\n        c = pat[i]\n        i = i + 1\n        if c == '*':\n            if i < n and pat[i] == '*':\n                # is some flavor of \"**\"\n                i = i + 1\n                # Treat **/ as ** so eat the \"/\"\n                if i < n and pat[i] == '/':\n                    i = i + 1\n                if i >= n:\n                    # is \"**EOF\" - to align with .gitignore just accept all\n                    res = res + '.*'\n                else:\n                    # is \"**\"\n                    # Note that this allows for any # of /'s (even 0) because\n                    # the .* will eat everything, even /'s\n                    res = res + '(.*/)?'\n            else:\n                # is \"*\" so map it to anything but \"/\"\n                res = res + '[^/]*'\n        elif c == '?':\n            # \"?\" is any char except \"/\"\n            res = res + '[^/]'\n        elif c == '[':\n            j = i\n            if j < n and pat[j] == '!':\n                j = j + 1\n            if j < n and pat[j] == ']':\n                j = j + 1\n            while j < n and pat[j] != ']':\n                j = j + 1\n            if j >= n:\n                res = res + '\\\\['\n            else:\n                stuff = pat[i:j].replace('\\\\', '\\\\\\\\')\n                i = j + 1\n                if stuff[0] == '!':\n                    stuff = '^' + stuff[1:]\n                elif stuff[0] == '^':\n                    stuff = '\\\\' + stuff\n                res = '%s[%s]' % (res, stuff)\n        else:\n            res = res + re.escape(c)\n    return res + '$'\n", "path": "docker/utils/fnmatch.py"}]}