Wrapper-friednly `lndir`

lndir (xorg.lndir) is a convenient utility to create a “shadow directory” of symlinks, and is used to create “wrapped packages” in Nixpkgs.

However, when encountering symbolic link targeting the absolute path to a file/directory in the source directory (FROMDIR), it creates the same symbolic link targeting that file/directory in FROMDIR. For example:

$ tree /dev/shm/fromdir
/dev/shm/fromdir
└── test_dir
    ├── test_file
    ├── test_link1 -> /dev/shm/fromdir/test_dir/test_file
    └── test_link2 -> ./test_file

1 directory, 3 files

$ lndir /dev/shm/fromdir /dev/shm/todir_old
$ tree /dev/shm/todir_old/
/dev/shm/todir_old/
└── test_dir
    ├── test_file -> /dev/shm/fromdir/test_dir/test_file
    ├── test_link1 -> /dev/shm/fromdir/test_dir/test_file
    └── test_link2 -> ./test_file

1 directory, 3 files

When the shadow file (/dev/shm/todir_old/test_dir/test_file) is wrapped or replaced by a normal file, the change will only be picked up by /dev/shm/todir_old/test_dir/test_link2 but not /dev/shm/todir_old/test_dir/test_link1, which references an absolute-path-targeted symbolic link.

A solution is to replace the target path that begins with FROMDIR path with TODIR path.

$ nix-run-local tree /dev/shm/todir
/dev/shm/todir
└── test_dir
    ├── test_file -> /dev/shm/fromdir/test_dir/test_file
    ├── test_link1 -> /dev/shm/todir/test_dir/test_file
    └── test_link2 -> ./test_file

1 directory, 3 files

Here’s a simple BASH implementation:

FROMDIR="$1"
TODIR="$2"

LENGTH_FROMDIR="${#FROMDIR}"

while IFS= read -d $'\0' -r ORIGINAL_DIR; do
	mkdir "$TODIR${ORIGINAL_DIR:$LENGTH_FROMDIR}"
done < <(find "$FROMDIR" -mindepth 1 -type d -print0)

while IFS= read -d $'\0' -r ORIGINAL_FILE; do
	ln -s "$ORIGINAL_FILE" "$TODIR${ORIGINAL_FILE:$LENGTH_FROMDIR}"
done < <(find "$FROMDIR" -mindepth 1 -type f -print0)

while IFS= read -d $'\0' -r ORIGINAL_LINK; do
	NEW_SYMLINK="$TODIR${ORIGINAL_LINK:$LENGTH_FROMDIR}"
	ORIGINAL_TARGET="$($READLINK_COMMAND "$ORIGINAL_LINK")"
	LENGTH_ORIGINAL_TARGET="${#ORIGINAL_TARGET}"
	NEW_TARGET="$ORIGINAL_TARGET"
	if [[ "$LENGTH_ORIGINAL_TARGET" -ge "$LENGTH_FROMDIR" ]] \
	&& [[ "${ORIGINAL_TARGET:0:$LENGTH_FROMDIR}" == "$FROMDIR" ]]; then
		NEW_TARGET="$TODIR${ORIGINAL_TARGET:$LENGTH_FROMDIR}"
	fi
	$SYMLINK_COMMAND "$NEW_TARGET" "$NEW_SYMLINK"
done < <(find "$FROMDIR" -mindepth 1 -type l -print0)

A generalized version can be found here:

Would it be useful for packaging? Are there reasons that prevent xorg.lndir from doing so?