diff options
Diffstat (limited to 's2slib.py')
-rw-r--r-- | s2slib.py | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/s2slib.py b/s2slib.py new file mode 100644 index 0000000..409157e --- /dev/null +++ b/s2slib.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# File: s2slib.py +# Locations: +# /mnt/public/Support/Programs/shortcuts/ +# https://gitlab.com/bgstack15/s2s +# Author: bgstack15@gmail.com +# SPDX-License-Identifier: GPL-3.0 +# Startdate: 2021-02-11 20:00 +# Title: Shortcut To Symlink library +# Purpose: Convert .lnk files to POSIX symlinks within a single filesystem +# History: +# Usage: +# see s2s.py +# Reference: +# https://stackoverflow.com/questions/25146960/python-convert-back-slashes-to-forward-slashes/25147093#25147093 +# https://github.com/libyal/liblnk/blob/main/documentation/Windows%20Shortcut%20File%20(LNK)%20format.asciidoc#file_attribute_flags +# Improve: +# Handle absolute paths when relative paths are absent? +# Add a force=True option to overwrite existing symlink that points to something else +# Dependencies: +# devuan-req: python3-liblnk +import pylnk # python3-liblnk +import os, re, posixpath +from sys import stderr + +debuglevel = 8 + +# Functions +def eprint(*args, **kwargs): + print(*args, file=stderr, **kwargs) + +# to benefit from caching outside of the function, maybe +lnkregex = re.compile(".*\.lnk$") + +def replace_one_symlink( + lnkfile + , delete = False + , debuglevel = debuglevel + , dryrun = False + ): + + thislnk = pylnk.file() + basename = os.path.basename(lnkfile) + #workdir = os.path.dirname(lnkfile) # not used + + # Exit if this is not a .lnk file + if not re.match(lnkregex,basename): + eprint(f"Cannot operate on {lnkfile}") + return -1 + + thislnk.open(lnkfile) + + # Get relative path + # we are really expecting a relative path for every single one, because we are dealing with Windows shortcuts to files all on the same network share. + # Although I suppose a symlink could work if you could somehow map the expected SMB path to a POSIX filesystem + try: + relative_path = thislnk.relative_path + except: + eprint(f"No relative path for {lnkfile}, and abs path not yet implemented.") + return -1 + # and make it a Unix-style path + relative_path = posixpath.join(*thislnk.relative_path.split('\\')) + + # Not used for anything, but here if we want it. + linktype = "file" + if thislnk.file_attribute_flags & 0x10: + linktype = "dir" + + # Get preferred name, and strip any " - Shortcut" + pn = os.path.splitext(lnkfile)[:-1][0] # remove the .lnk part, but preserve any other dots + symlink_path = re.sub(' - Shortcut$', "",pn) + + # take action + if debuglevel >= 5: + eprint(f"Want symlink \"{symlink_path}\" ({linktype})--> \"{relative_path}\"") + + ready_to_delete = False + + if os.path.exists(symlink_path): + current_source = "" + try: + current_source = os.path.realpath(symlink_path) + except: + eprint(f"Unable to determine current_source for {symlink_path} which already exists.") + normpath = os.path.normpath(os.path.join(os.path.dirname(symlink_path),relative_path)) + #eprint(f"Please ensure {current_source} matches {relative_path} which is {normpath}") + if normpath != current_source: + # The existing symlink points to something else. Not sure how to proceed. + # probably need to add a --force option to overwrite + eprint(f"Warning: Existing symlink {symlink_path} points to {current_source} and not {normpath} as requested") + else: + # it exists and already points to what we want it to point to. + if debuglevel >= 3: + eprint(f"symlink {symlink_path} already points to {current_source} which is {normpath}") + ready_to_delete = True + else: + # symlink does not exist, so make it! + if debuglevel >= 1: + eprint(f"ln -s {relative_path} {symlink_path}") + if not dryrun: + try: + os.symlink(relative_path,symlink_path) + ready_to_delete = True + except Exception as e: + eprint(e) + + if delete: + if ready_to_delete: + if debuglevel >= 2: + eprint(f"rm {lnkfile}") + if not dryrun: + os.remove(lnkfile) + return 0 + +def list_all_child_lnk_files(indir): + mylist = [] + for root, dirs, files in os.walk(indir): + for name in files: + if ".lnk" in name: + mylist.append(os.path.join(root,name)) + #eprint(os.path.join(root,name)) + return mylist + +def replace_all_symlinks( + indir + , delete = False + , debuglevel = debuglevel + , dryrun = False + ): + result = 0 + + # get list of all lnk files + mylist = list_all_child_lnk_files(indir) + for lnkfile in mylist: + replace_one_symlink( + lnkfile + , delete = delete + , debuglevel = debuglevel + , dryrun = dryrun + ) + + return result + +if __name__ == "__main__": + print("running the main loop") |