+ for flag in options:
+ (option, parameter) = flag
+ if option == '--exclude':
+ excluded_paths.append(resolve_parent(parameter))
+ elif option == '--exclude-from':
+ try:
+ f = open(resolve_parent(parameter))
+ except IOError as e:
+ raise fatal("couldn't read %s" % parameter)
+ for exclude_path in f.readlines():
+ # FIXME: perhaps this should be rstrip('\n')
+ exclude_path = resolve_parent(exclude_path.strip())
+ if exclude_path:
+ excluded_paths.append(exclude_path)
+ return sorted(frozenset(excluded_paths))
+
+
+def parse_rx_excludes(options, fatal):
+ """Traverse the options and extract all rx excludes, or call
+ Option.fatal()."""
+ excluded_patterns = []
+
+ for flag in options:
+ (option, parameter) = flag
+ if option == '--exclude-rx':
+ try:
+ excluded_patterns.append(re.compile(parameter))
+ except re.error as ex:
+ fatal('invalid --exclude-rx pattern (%s): %s' % (parameter, ex))
+ elif option == '--exclude-rx-from':
+ try:
+ f = open(resolve_parent(parameter))
+ except IOError as e:
+ raise fatal("couldn't read %s" % parameter)
+ for pattern in f.readlines():
+ spattern = pattern.rstrip('\n')
+ if not spattern:
+ continue
+ try:
+ excluded_patterns.append(re.compile(spattern))
+ except re.error as ex:
+ fatal('invalid --exclude-rx pattern (%s): %s' % (spattern, ex))
+ return excluded_patterns
+
+
+def should_rx_exclude_path(path, exclude_rxs):
+ """Return True if path matches a regular expression in exclude_rxs."""
+ for rx in exclude_rxs:
+ if rx.search(path):
+ debug1('Skipping %r: excluded by rx pattern %r.\n'
+ % (path, rx.pattern))
+ return True
+ return False
+
+
+# FIXME: Carefully consider the use of functions (os.path.*, etc.)
+# that resolve against the current filesystem in the strip/graft
+# functions for example, but elsewhere as well. I suspect bup's not
+# always being careful about that. For some cases, the contents of
+# the current filesystem should be irrelevant, and consulting it might
+# produce the wrong result, perhaps via unintended symlink resolution,
+# for example.
+
+def path_components(path):
+ """Break path into a list of pairs of the form (name,
+ full_path_to_name). Path must start with '/'.
+ Example:
+ '/home/foo' -> [('', '/'), ('home', '/home'), ('foo', '/home/foo')]"""
+ if not path.startswith(b'/'):
+ raise Exception('path must start with "/": %s' % path_msg(path))
+ # Since we assume path startswith('/'), we can skip the first element.
+ result = [(b'', b'/')]
+ norm_path = os.path.abspath(path)
+ if norm_path == b'/':
+ return result
+ full_path = b''
+ for p in norm_path.split(b'/')[1:]:
+ full_path += b'/' + p
+ result.append((p, full_path))
+ return result
+
+
+def stripped_path_components(path, strip_prefixes):
+ """Strip any prefix in strip_prefixes from path and return a list
+ of path components where each component is (name,
+ none_or_full_fs_path_to_name). Assume path startswith('/').
+ See thelpers.py for examples."""
+ normalized_path = os.path.abspath(path)
+ sorted_strip_prefixes = sorted(strip_prefixes, key=len, reverse=True)
+ for bp in sorted_strip_prefixes:
+ normalized_bp = os.path.abspath(bp)
+ if normalized_bp == b'/':
+ continue
+ if normalized_path.startswith(normalized_bp):
+ prefix = normalized_path[:len(normalized_bp)]
+ result = []
+ for p in normalized_path[len(normalized_bp):].split(b'/'):
+ if p: # not root
+ prefix += b'/'
+ prefix += p
+ result.append((p, prefix))
+ return result
+ # Nothing to strip.
+ return path_components(path)
+
+
+def grafted_path_components(graft_points, path):
+ # Create a result that consists of some number of faked graft
+ # directories before the graft point, followed by all of the real
+ # directories from path that are after the graft point. Arrange
+ # for the directory at the graft point in the result to correspond
+ # to the "orig" directory in --graft orig=new. See t/thelpers.py
+ # for some examples.
+
+ # Note that given --graft orig=new, orig and new have *nothing* to
+ # do with each other, even if some of their component names
+ # match. i.e. --graft /foo/bar/baz=/foo/bar/bax is semantically
+ # equivalent to --graft /foo/bar/baz=/x/y/z, or even
+ # /foo/bar/baz=/x.
+
+ # FIXME: This can't be the best solution...
+ clean_path = os.path.abspath(path)