michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: import posixpath michael@0: import os michael@0: import re michael@0: michael@0: ''' michael@0: Like os.path, with a reduced set of functions, and with normalized path michael@0: separators (always use forward slashes). michael@0: Also contains a few additional utilities not found in os.path. michael@0: ''' michael@0: michael@0: michael@0: def normsep(path): michael@0: ''' michael@0: Normalize path separators, by using forward slashes instead of whatever michael@0: os.sep is. michael@0: ''' michael@0: if os.sep != '/': michael@0: path = path.replace(os.sep, '/') michael@0: return path michael@0: michael@0: michael@0: def relpath(path, start): michael@0: rel = normsep(os.path.relpath(path, start)) michael@0: return '' if rel == '.' else rel michael@0: michael@0: michael@0: def abspath(path): michael@0: return normsep(os.path.abspath(path)) michael@0: michael@0: michael@0: def join(*paths): michael@0: return normsep(os.path.join(*paths)) michael@0: michael@0: michael@0: def normpath(path): michael@0: return posixpath.normpath(normsep(path)) michael@0: michael@0: michael@0: def dirname(path): michael@0: return posixpath.dirname(normsep(path)) michael@0: michael@0: michael@0: def commonprefix(paths): michael@0: return posixpath.commonprefix([normsep(path) for path in paths]) michael@0: michael@0: michael@0: def basename(path): michael@0: return os.path.basename(path) michael@0: michael@0: michael@0: def splitext(path): michael@0: return posixpath.splitext(normsep(path)) michael@0: michael@0: michael@0: def split(path): michael@0: ''' michael@0: Return the normalized path as a list of its components. michael@0: split('foo/bar/baz') returns ['foo', 'bar', 'baz'] michael@0: ''' michael@0: return normsep(path).split('/') michael@0: michael@0: michael@0: def basedir(path, bases): michael@0: ''' michael@0: Given a list of directories (bases), return which one contains the given michael@0: path. If several matches are found, the deepest base directory is returned. michael@0: basedir('foo/bar/baz', ['foo', 'baz', 'foo/bar']) returns 'foo/bar' michael@0: ('foo' and 'foo/bar' both match, but 'foo/bar' is the deepest match) michael@0: ''' michael@0: path = normsep(path) michael@0: bases = [normsep(b) for b in bases] michael@0: if path in bases: michael@0: return path michael@0: for b in sorted(bases, reverse=True): michael@0: if b == '' or path.startswith(b + '/'): michael@0: return b michael@0: michael@0: michael@0: re_cache = {} michael@0: michael@0: def match(path, pattern): michael@0: ''' michael@0: Return whether the given path matches the given pattern. michael@0: An asterisk can be used to match any string, including the null string, in michael@0: one part of the path: michael@0: 'foo' matches '*', 'f*' or 'fo*o' michael@0: However, an asterisk matching a subdirectory may not match the null string: michael@0: 'foo/bar' does *not* match 'foo/*/bar' michael@0: If the pattern matches one of the ancestor directories of the path, the michael@0: patch is considered matching: michael@0: 'foo/bar' matches 'foo' michael@0: Two adjacent asterisks can be used to match files and zero or more michael@0: directories and subdirectories. michael@0: 'foo/bar' matches 'foo/**/bar', or '**/bar' michael@0: ''' michael@0: if not pattern: michael@0: return True michael@0: if not pattern in re_cache: michael@0: pattern = re.escape(pattern) michael@0: pattern = re.sub(r'(^|\\\/)\\\*\\\*\\\/', r'\1(?:.+/)?', pattern) michael@0: pattern = re.sub(r'(^|\\\/)\\\*\\\*$', r'(?:\1.+)?', pattern) michael@0: pattern = pattern.replace(r'\*', '[^/]*') + '(?:/.*)?$' michael@0: re_cache[pattern] = re.compile(pattern) michael@0: return re_cache[pattern].match(path) is not None michael@0: michael@0: michael@0: def rebase(oldbase, base, relativepath): michael@0: ''' michael@0: Return relativepath relative to base instead of oldbase. michael@0: ''' michael@0: if base == oldbase: michael@0: return relativepath michael@0: if len(base) < len(oldbase): michael@0: assert basedir(oldbase, [base]) == base michael@0: relbase = relpath(oldbase, base) michael@0: result = join(relbase, relativepath) michael@0: else: michael@0: assert basedir(base, [oldbase]) == oldbase michael@0: relbase = relpath(base, oldbase) michael@0: result = relpath(relativepath, relbase) michael@0: result = normpath(result) michael@0: if relativepath.endswith('/') and not result.endswith('/'): michael@0: result += '/' michael@0: return result