#! /usr/bin/env python2.4 """Resolves symbolic links. "snl" = "ln -s" backwards. Usage: snl [-s] [-r] ... For each path given on the command line, Flags: -s suppress printing of paths which are not symlinks -1 only perform one round of resolution (by default, resolve all the way down) """ import sys import getopt import os RECURSION_LIMIT = 100 def deepreadlink(path): """Takes an absolute path and, if it or any of its parent directories are links, returns the target of the link. If there are no links to be had, returns None. """ if (os.path.islink(path)): return os.readlink(path) else: dir, name = os.path.split(path) assert (dir != ""), "attempt to read relative path: " + path if (dir == "/"): return None tgtdir = deepreadlink(dir) if (tgtdir == None): return None return os.path.join(tgtdir, name) def resolve(path, recurse=True, recursionLimit=RECURSION_LIMIT): if (recursionLimit == 0): raise ValueError, "recursion limit exceeded at path: " + path if (not os.path.lexists(path)): raise ValueError, "path does not exist: " + path path = os.path.abspath(path) tgtpath = deepreadlink(path) if (tgtpath == None): return path if (tgtpath == path): raise ValueError, "self-referential link: " + path if (recurse): return resolve(tgtpath, True, (recursionLimit - 1)) else: return tgtpath def err(*msg): sys.stderr.write((" ".join(map(str, msg)) + "\n")) if (__name__ == "__main__"): try: flags, args = getopt.gnu_getopt(sys.argv[1:], "s1") if (len(args) == 0): raise getopt.GetoptError, "no arguments supplied" suppress = ("-s", "") in flags recurse = ("-1", "") not in flags for path in args: try: rpath = resolve(path, recurse) if (suppress and (path == rpath)): continue print rpath except ValueError, e: err((path + ":"), e) except getopt.GetoptError, e: print "Error:", e print print __doc__