X-Git-Url: https://iankelling.org/git/?p=lnf;a=blobdiff_plain;f=lnf;h=2518c1c4ac60cf35147ba72bdc886c252759bbe2;hp=104c90187008bfd3caa40d9588ff44cd4d1a4f38;hb=df814ed22cf0dfb2f93cc4c228099d36c40b6fae;hpb=cca94a4f6e8acc20eacb8219b5f910e89b312802 diff --git a/lnf b/lnf index 104c901..2518c1c 100755 --- a/lnf +++ b/lnf @@ -1,59 +1,104 @@ #!/bin/bash -# Copyright (C) 2014 Ian Kelling +# Copyright (C) 2014-2016 Ian Kelling # This program is under GPL v. 3 or later, see lnf() { - local help="lnf [--help] [-T] LN_ARGUMENT... -Create symlinks conveniently and forcefully. - -Remove existing file/links using trash-put or rm -rf if it is not available. -Create directory if needed. Small change to ln argument semantics: for 2 arguments, -the second argument is a directory unless -T is passed. This removes ambiguity of -replacing a file but not a directory. No ln option arguments are supported." - - if [[ $1 == --help || $# -eq 0 ]]; then + local help="Usage: + lnf -T TARGET LINK_NAME (1st form) + lnf TARGET (2nd form) + lnf TARGET... DIRECTORY (3rd form) +Create symlinks forcefully + +Removes existing files using trash-put or rm -rf if it is not available, +or trash-put fails due to a limitation such as a cross-filesystem link. +Create directory if needed. Slightly more restrictive arguments than ln. + +In the 1st form, create a link to TARGET with the name LINK_NAME. In the 2nd +form, create a link to TARGET in the current directory. In the 3rd form, create +links to each TARGET in DIRECTORY." + + if [[ $1 == --help || $1 == -h || $# -eq 0 ]]; then echo "$help" return 0 fi - local nodir=false + local nodir if [[ $1 == -T ]]; then - nodir=true + nodir=-T shift + if (( $# != 2 )); then + echo "lnf error: expected 2 arguments with -T flag. Got $#" + return 1 + fi fi - - - local remove x - if type -P dircolors >/dev/null; then - remove=trash-put - else - remove="rm -rf" - fi - - if $nodir && [[ $# -eq 2 ]]; then - if [[ -e "$2" || -L "$2" ]]; then - $remove "$2" - elif [[ ! -d "$2/.." ]]; then - mkdir -p "$2/.." + + local reset_extglob=false + ! shopt extglob >/dev/null && reset_extglob=true + shopt -s extglob + + + local x ret prefix dir to_remove + local mkdir=false + + to_remove=() + if [[ $nodir ]]; then + dir="$(dirname "$2")" + if [[ -e $2 || -L $2 ]]; then + to_remove+=("$2") + elif [[ ! -d $dir ]]; then + mkdir=true + if [[ -e $dir || -L $dir ]]; then + to_remove+=("$dir") + fi fi - elif [[ $# -ge 2 ]]; then + elif (( $# >= 2 )); then if [[ -d ${!#} ]]; then - local oldcwd=$PWD - cd ${!#} # last arg - for x in "${@:1:$(($#-1))}"; do # all but last arg - # remove any trailing slashes + prefix="${!#}/" # last arg + for x in "${@:1:$(( $# - 1 ))}"; do # all but last arg + # Remove 1 or more trailing slashes, using. x="${x%%+(/)}" - # remove any leading directory components - x="${x##*/}" - [[ -e "$x" || -L "$x" ]] && $remove "$x" + # remove any leading directory components, add prefix + x="$prefix/${x##*/}" + [[ -e "$x" || -L "$x" ]] && to_remove+=("$x") done - cd "$oldcwd" else - mkdir -p "${!#}" + if ! mkdir -p "${!#}"; then + echo "lnf error: failed to make directory ${!#}" + return 1 + fi fi elif [[ $# -eq 1 ]]; then - [[ -e "${1##*/}" || -L "${1##*/}" ]] && $remove "${1##*/}" + [[ -e "${1##*/}" || -L "${1##*/}" ]] && to_remove+=("${1##*/}") fi - ln -s -- "$@" + if (( ${#to_remove[@]} >= 1 )); then + if type -P trash-put >/dev/null; then + trash-put -- "${to_remove[@]}" || ret=$? + # trash-put will fail to trash a link that goes across filesystems (72), + # and for empty files (74) + # so revert to rm -rf in that case + if [[ $ret == 72 ]]; then + echo "$0: using rm -rf to overcome cross filesystem trash-put limitation" + rm -rf -- "${to_remove[@]}" + elif [[ $ret == 74 ]]; then + echo "$0: using rm -rf to overcome empty file & hardlink trash-put limitation" + rm -rf -- "${to_remove[@]}" + elif [[ $ret && $ret != 0 ]]; then + return $x + fi + else + rm -rf -- "${to_remove[@]}" + fi + fi + + $reset_extglob && shopt -u extglob + + if $mkdir; then + if ! mkdir -p "$(dirname "$2")"; then + echo "lnf error: failed to make directory $(dirname "$2")" + return 1 + fi + fi + + ln -s $nodir -- "$@" } lnf "$@"