several bug fixes, simplify to just one script file
[lnf] / lnf
1 #!/bin/bash
2 # Copyright (C) 2014 Ian Kelling
3 # This program is under GPL v. 3 or later, see <http://www.gnu.org/licenses/>
4
5 lnf() {
6 local help="lnf - Create symlinks conveniently and forcefully.
7 Usage:
8 lnf -T TARGET LINK_NAME (1st form)
9 lnf TARGET (2nd form)
10 lnf TARGET... DIRECTORY (3rd form)
11
12 Remove existing file in the using trash-put or rm -rf if it is not available,
13 or trash-put fails due to a cross-filesystem link.
14 Create directory if needed. Slightly more restrictive arguments than ln.
15
16 In the 1st form, create a link to TARGET with the name LINK_NAME. In the 2nd
17 form, create a link to TARGET in the current directory. In the 3rd form, create
18 links to each TARGET in DIRECTORY."
19
20 if [[ $1 == --help || $# -eq 0 ]]; then
21 echo "$help"
22 return 0
23 fi
24
25 local nodir
26 if [[ $1 == -T ]]; then
27 nodir=-T
28 shift
29 if (( $# != 2 )); then
30 echo "lnf error: expected 2 arguments with -T flag. Got $#"
31 return 1
32 fi
33 fi
34
35 local reset_extglob=false
36 ! shopt extglob >/dev/null && reset_extglob=true
37 shopt -s extglob
38
39
40 local x ret prefix dir to_remove
41
42 to_remove=()
43 if [[ $nodir ]]; then
44 dir="$(dirname "$2")"
45 if [[ -e $2 || -L $2 ]]; then
46 to_remove+=("$2")
47 elif [[ ! -d $dir ]]; then
48 if [[ -e $dir || -L $dir ]]; then
49 to_remove+=("$dir")
50 fi
51 if ! mkdir -p "$(dirname "$2")"; then
52 echo "lnf error: failed to make directory $(dirname "$2")"
53 return 1
54 fi
55 fi
56 elif (( $# >= 2 )); then
57 if [[ -d ${!#} ]]; then
58 prefix="${!#}/" # last arg
59 for x in "${@:1:$(( $# - 1 ))}"; do # all but last arg
60 # Remove 1 or more trailing slashes, using.
61 x="${x%%+(/)}"
62 # remove any leading directory components, add prefix
63 x="$prefix/${x##*/}"
64 [[ -e "$x" || -L "$x" ]] && to_remove+=("$x")
65 done
66 else
67 if ! mkdir -p "${!#}"; then
68 echo "lnf error: failed to make directory ${!#}"
69 return 1
70 fi
71 fi
72 elif [[ $# -eq 1 ]]; then
73 [[ -e "${1##*/}" || -L "${1##*/}" ]] && to_remove+=("${1##*/}")
74 fi
75 if (( ${#to_remove[@]} >= 1 )); then
76 if type -P trash-put >/dev/null; then
77 trash-put -- "${to_remove[@]}" || ret=$?
78 # trash-put will fail to trash a link that goes across filesystems (72),
79 # and for empty files (74)
80 # so revert to rm -rf in that case
81 if [[ $ret == 72 ]]; then
82 echo "$0: using rm -rf to overcome cross filesystem trash-put limitation"
83 rm -rf -- "${to_remove[@]}"
84 elif [[ $ret == 74 ]]; then
85 echo "$0: using rm -rf to overcome empty file/dir trash-put limitation"
86 rm -rf -- "${to_remove[@]}"
87 elif [[ $ret && $ret != 0 ]]; then
88 return $x
89 fi
90 else
91 rm -rf -- "${to_remove[@]}"
92 fi
93 fi
94
95 $reset_extglob && shopt -u extglob
96 ln -s $nodir -- "$@"
97 }
98 lnf "$@"