small doc update
[lnf] / lnf
1 #!/bin/bash
2 # Copyright (C) 2014-2016 Ian Kelling
3 # This program is under GPL v. 3 or later, see <http://www.gnu.org/licenses/>
4
5 lnf() {
6 local help="Usage:
7 lnf -T TARGET LINK_NAME (1st form)
8 lnf TARGET (2nd form)
9 lnf TARGET... DIRECTORY (3rd form)
10 Create symlinks forcefully
11
12 Removes existing files using trash-put or rm -rf if it is not available,
13 or trash-put fails due to a limitation such as 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 local mkdir=false
42
43 to_remove=()
44 if [[ $nodir ]]; then
45 dir="$(dirname "$2")"
46 if [[ -e $2 || -L $2 ]]; then
47 to_remove+=("$2")
48 elif [[ ! -d $dir ]]; then
49 mkdir=true
50 if [[ -e $dir || -L $dir ]]; then
51 to_remove+=("$dir")
52 fi
53 fi
54 elif (( $# >= 2 )); then
55 if [[ -d ${!#} ]]; then
56 prefix="${!#}/" # last arg
57 for x in "${@:1:$(( $# - 1 ))}"; do # all but last arg
58 # Remove 1 or more trailing slashes, using.
59 x="${x%%+(/)}"
60 # remove any leading directory components, add prefix
61 x="$prefix/${x##*/}"
62 [[ -e "$x" || -L "$x" ]] && to_remove+=("$x")
63 done
64 else
65 if ! mkdir -p "${!#}"; then
66 echo "lnf error: failed to make directory ${!#}"
67 return 1
68 fi
69 fi
70 elif [[ $# -eq 1 ]]; then
71 [[ -e "${1##*/}" || -L "${1##*/}" ]] && to_remove+=("${1##*/}")
72 fi
73 if (( ${#to_remove[@]} >= 1 )); then
74 if type -P trash-put >/dev/null; then
75 trash-put -- "${to_remove[@]}" || ret=$?
76 # trash-put will fail to trash a link that goes across filesystems (72),
77 # and for empty files (74)
78 # so revert to rm -rf in that case
79 if [[ $ret == 72 ]]; then
80 echo "$0: using rm -rf to overcome cross filesystem trash-put limitation"
81 rm -rf -- "${to_remove[@]}"
82 elif [[ $ret == 74 ]]; then
83 echo "$0: using rm -rf to overcome empty file & hardlink trash-put limitation"
84 rm -rf -- "${to_remove[@]}"
85 elif [[ $ret && $ret != 0 ]]; then
86 return $x
87 fi
88 else
89 rm -rf -- "${to_remove[@]}"
90 fi
91 fi
92
93 $reset_extglob && shopt -u extglob
94
95 if $mkdir; then
96 if ! mkdir -p "$(dirname "$2")"; then
97 echo "lnf error: failed to make directory $(dirname "$2")"
98 return 1
99 fi
100 fi
101
102 ln -s $nodir -- "$@"
103 }
104 lnf "$@"