2 # Copyright (C) 2014-2016 Ian Kelling
3 # This program is under GPL v. 3 or later, see <http://www.gnu.org/licenses/>
6 local target dest_file dest_dir
10 if [[ -L $dest_file ]]; then
11 if [[ $
(readlink
$dest_file) == "$target" ]]; then
12 # Leave the link in place, but make sure it's
13 # owner & group is as if we created it.
14 # links all get 777 perms, so
15 # we already know that is right.
18 if [[ $
(stat
-L -c%a
"$dest_dir") == 2???
]]; then
19 grp
=$
(stat
-L -c%g
"$dest_dir") ||
return $?
21 grp
=$
(id
-g) ||
return $?
23 if [[ $EUID == 0 && $
(stat
-c%u
"$dest_file") != 0 ]]; then
24 chown
-h 0:$grp "$dest_file" ||
return $?
25 elif [[ $
(stat
-c%g
"$dest_file") != "$grp" ]]; then
26 chgrp
-h $grp "$dest_file" ||
return $?
31 to_remove
+=("$dest_file")
32 elif [[ -e $dest_file ]]; then
33 to_remove
+=("$dest_file")
39 lnf [OPTIONS] -T TARGET LINK_NAME (1st form)
40 lnf [OPTIONS] TARGET (2nd form)
41 lnf [OPTIONS] TARGET... DIRECTORY (3rd form)
42 Create symlinks forcefully
44 If the link already exists, make it's ownership be the same as if it was
45 newly created (only chown if we are root). Removes existing files using
46 trash-put or rm -rf if it is not available, or if trash-put fails due to a
47 limitation such as a cross-filesystem link. Create directory of link if
48 needed. Slightly more restrictive arguments than ln.
50 In the 1st form, create a link to TARGET with the name LINK_NAME. In the 2nd
51 form, create a link to TARGET in the current directory. In the 3rd form, create
52 links to each TARGET in DIRECTORY.
54 -n|--dry-run Do verbose dry run.
55 -v|--verbose Print commands which modify the filesystem.
56 -h|--help Print help and exit.
64 temp
=$
(getopt
-l help,dry-run
,verbose hnTv
"$@") || usage
1
68 -n|
--dry-run) dry_run
=true
; verbose
=true
; shift ;;
69 -T) nodir
=-T; shift ;;
70 -v|
--verbose) verbose
=true
; shift ;;
71 -h|
--help) echo "$help"; return 0 ;;
73 *) echo "$0: Internal error! unexpected args: $*" ; exit 1 ;;
77 if (( $# == 0 )); then
83 if (( $# != 2 )); then
84 echo "lnf: error: expected 2 arguments with -T flag. Got $#"
89 local reset_extglob
=false
90 ! shopt extglob
>/dev
/null
&& reset_extglob
=true
94 local -a to_remove to_link
95 local x ret prefix dest_dir grp target
100 dest_dir
="$(dirname "$dest_file")"
101 _lnf_existing_link
"$1" "$dest_file" "$dest_dir" ||
return $?
102 if $do_exit; then return 0; fi
103 if [[ ! -d $dest_dir ]]; then
105 if [[ -e $dest_dir ||
-L $dest_dir ]]; then
106 to_remove
+=("$dest_dir")
109 to_link
+=("$dest_file")
110 elif (( $# >= 2 )); then
112 if [[ -d $dest_dir ]]; then
113 prefix
="$dest_dir" # last arg
114 for target
in "${@:1:$(( $# - 1 ))}"; do # all but last arg
115 # Remove 1 or more trailing slashes, using.
116 dest_file
="${target%%+(/)}"
117 # remove any leading directory components, add prefix
118 dest_file
="$prefix/${target##*/}"
119 _lnf_existing_link
"$target" "$dest_file" "$dest_dir"
122 to_link
+=("${@:1:$(( $# - 1 ))}")
125 if (( ${#to_link[@]} == 0 )); then
128 to_link
+=("$dest_dir")
129 elif [[ $# -eq 1 ]]; then
131 _lnf_existing_link
"$1" "$dest_file" . ||
return $?
132 if $do_exit; then return 0; fi
134 if (( ${#to_remove[@]} >= 1 )); then
135 if type -P trash-put
>/dev
/null
; then
137 echo "lnf: trash-put -- ${to_remove[*]}"
140 trash-put
-- "${to_remove[@]}" || ret
=$?
142 # trash-put will fail to trash a link that goes across filesystems (72),
143 # and for empty files (74)
144 # so revert to rm -rf in that case
145 if [[ $ret == 72 ]]; then
146 echo "lnf: using rm -rf to overcome cross filesystem trash-put limitation"
147 rm -rf -- "${to_remove[@]}" ||
return $?
148 elif [[ $ret == 74 ]]; then
149 echo "lnf: using rm -rf to overcome empty file & hardlink trash-put limitation"
150 rm -rf -- "${to_remove[@]}"
151 elif [[ $ret && $ret != 0 ]]; then
156 echo "lnf: rm -rf -- ${to_remove[*]}"
159 rm -rf -- "${to_remove[@]}"
164 $reset_extglob && shopt -u extglob
168 echo "lnf: mkdir -p $dest_dir"
171 if ! $dry_run && ! mkdir
-p "$dest_dir"; then
172 echo "lnf error: failed to make directory $dest_dir"
178 echo "lnf: ln -s $nodir -- ${to_link[*]}"
181 ln -s $nodir -- "${to_link[@]}"