#!/usr/bin/env python # relpath: Find the relative path between a directory and one or more paths. # relsymlink: Make a symlink to a path via relative path. # # By Nathaniel Gray from os.path import * import os def relpath(p1, target, unsafe=False): "relpath(p1, target): Find the relative path between directory p1 and path target" if not isabs(p1): p1 = abspath(p1) if not unsafe and not isdir(p1): raise ValueError, "Not a directory: %s" % p1 if not isabs(target): target = abspath(target) p1 = normpath(p1) p2 = normpath(target) # Special-case p1 = '/' now because we'll have to later anyway. if p1 == sep: return '.' + p2 p1l = p1.split(sep) p2l = p2.split(sep) i = 0 len1 = len(p1l) top = min([len1, len(p2l)]) while i < top and p1l[i] == p2l[i]: i+=1 if i == len1: return '.' + sep + sep.join(p2l[i:]) else: return ('..' + sep) * (len1 - i) + sep.join(p2l[i:]) def relsymlink(points_to, f, unsafe=False): rp = relpath(f, points_to, unsafe) return os.symlink(rp, f) if __name__ == "__main__": import sys, optparse script = basename(sys.argv[0]) def make_parser( usage ): parser = optparse.OptionParser( usage ) parser.add_option('-u', '--unsafe', action="store_true", help="Don't verify that from_dir is a directory (for speed)") return parser if script == "relpath": usage = \ """usage: %prog from_dir to_path1 [to_path2 ...] Calculate the relative path(s) from directory from_dir to a set of paths. Prints one path per line on stdout.""" parser = make_parser(usage) options, args = parser.parse_args() if len(args) < 2: parser.error( "not enough arguments" ) try: for a in args[1:]: print relpath(args[0], a, unsafe=options.unsafe) except ValueError, msg: sys.stderr.write(sys.argv[0] + ': ' + msg[0] + '\n' ) sys.exit(1) sys.exit(0) elif script == "lnrel": usage = \ """usage: %prog target1 [target2 ...] from_dir %prog target1 from_file Create symlinks using calculated relative paths.""" parser = make_parser(usage) options, args = parser.parse_args() if len(args) < 2: parser.error( "not enough arguments" ) try: if not isdir(args[-1]): # This should be the "lnrel file1 file2" case if len(args) != 2: parser.error( "too many arguments" ) print args[0], "<-", args[1] relsymlink(args[0], args[1], False) else: # In this case we need to append the filename from each target # to from_dir from_dir = args[-1] # Do the first target manually, using options.unsafe fname = basename(args[0]) print args[0], "<-", join(from_dir, fname) relsymlink(args[0], join(from_dir, fname), unsafe=options.unsafe) for target in args[1:-1]: # Always do the rest unsafely fname = basename(target) relsymlink(target, join(from_dir,fname), unsafe=True) except ValueError, msg: sys.stderr.write(sys.argv[0] + ': ' + msg[0] + '\n' ) sys.exit(1) sys.exit(0) else: print """Error: relpath called with an unrecognized name. It should be called by the name `relpath' or `lnrel'."""