|
1 # This Source Code Form is subject to the terms of the Mozilla Public |
|
2 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 |
|
5 import posixpath |
|
6 import os |
|
7 import re |
|
8 |
|
9 ''' |
|
10 Like os.path, with a reduced set of functions, and with normalized path |
|
11 separators (always use forward slashes). |
|
12 Also contains a few additional utilities not found in os.path. |
|
13 ''' |
|
14 |
|
15 |
|
16 def normsep(path): |
|
17 ''' |
|
18 Normalize path separators, by using forward slashes instead of whatever |
|
19 os.sep is. |
|
20 ''' |
|
21 if os.sep != '/': |
|
22 path = path.replace(os.sep, '/') |
|
23 return path |
|
24 |
|
25 |
|
26 def relpath(path, start): |
|
27 rel = normsep(os.path.relpath(path, start)) |
|
28 return '' if rel == '.' else rel |
|
29 |
|
30 |
|
31 def abspath(path): |
|
32 return normsep(os.path.abspath(path)) |
|
33 |
|
34 |
|
35 def join(*paths): |
|
36 return normsep(os.path.join(*paths)) |
|
37 |
|
38 |
|
39 def normpath(path): |
|
40 return posixpath.normpath(normsep(path)) |
|
41 |
|
42 |
|
43 def dirname(path): |
|
44 return posixpath.dirname(normsep(path)) |
|
45 |
|
46 |
|
47 def commonprefix(paths): |
|
48 return posixpath.commonprefix([normsep(path) for path in paths]) |
|
49 |
|
50 |
|
51 def basename(path): |
|
52 return os.path.basename(path) |
|
53 |
|
54 |
|
55 def splitext(path): |
|
56 return posixpath.splitext(normsep(path)) |
|
57 |
|
58 |
|
59 def split(path): |
|
60 ''' |
|
61 Return the normalized path as a list of its components. |
|
62 split('foo/bar/baz') returns ['foo', 'bar', 'baz'] |
|
63 ''' |
|
64 return normsep(path).split('/') |
|
65 |
|
66 |
|
67 def basedir(path, bases): |
|
68 ''' |
|
69 Given a list of directories (bases), return which one contains the given |
|
70 path. If several matches are found, the deepest base directory is returned. |
|
71 basedir('foo/bar/baz', ['foo', 'baz', 'foo/bar']) returns 'foo/bar' |
|
72 ('foo' and 'foo/bar' both match, but 'foo/bar' is the deepest match) |
|
73 ''' |
|
74 path = normsep(path) |
|
75 bases = [normsep(b) for b in bases] |
|
76 if path in bases: |
|
77 return path |
|
78 for b in sorted(bases, reverse=True): |
|
79 if b == '' or path.startswith(b + '/'): |
|
80 return b |
|
81 |
|
82 |
|
83 re_cache = {} |
|
84 |
|
85 def match(path, pattern): |
|
86 ''' |
|
87 Return whether the given path matches the given pattern. |
|
88 An asterisk can be used to match any string, including the null string, in |
|
89 one part of the path: |
|
90 'foo' matches '*', 'f*' or 'fo*o' |
|
91 However, an asterisk matching a subdirectory may not match the null string: |
|
92 'foo/bar' does *not* match 'foo/*/bar' |
|
93 If the pattern matches one of the ancestor directories of the path, the |
|
94 patch is considered matching: |
|
95 'foo/bar' matches 'foo' |
|
96 Two adjacent asterisks can be used to match files and zero or more |
|
97 directories and subdirectories. |
|
98 'foo/bar' matches 'foo/**/bar', or '**/bar' |
|
99 ''' |
|
100 if not pattern: |
|
101 return True |
|
102 if not pattern in re_cache: |
|
103 pattern = re.escape(pattern) |
|
104 pattern = re.sub(r'(^|\\\/)\\\*\\\*\\\/', r'\1(?:.+/)?', pattern) |
|
105 pattern = re.sub(r'(^|\\\/)\\\*\\\*$', r'(?:\1.+)?', pattern) |
|
106 pattern = pattern.replace(r'\*', '[^/]*') + '(?:/.*)?$' |
|
107 re_cache[pattern] = re.compile(pattern) |
|
108 return re_cache[pattern].match(path) is not None |
|
109 |
|
110 |
|
111 def rebase(oldbase, base, relativepath): |
|
112 ''' |
|
113 Return relativepath relative to base instead of oldbase. |
|
114 ''' |
|
115 if base == oldbase: |
|
116 return relativepath |
|
117 if len(base) < len(oldbase): |
|
118 assert basedir(oldbase, [base]) == base |
|
119 relbase = relpath(oldbase, base) |
|
120 result = join(relbase, relativepath) |
|
121 else: |
|
122 assert basedir(base, [oldbase]) == oldbase |
|
123 relbase = relpath(base, oldbase) |
|
124 result = relpath(relativepath, relbase) |
|
125 result = normpath(result) |
|
126 if relativepath.endswith('/') and not result.endswith('/'): |
|
127 result += '/' |
|
128 return result |