X-Git-Url: https://iankelling.org/git/?p=lnf;a=blobdiff_plain;f=lnf;fp=lnf;h=8a9df12631969a491bee07b1abad07685aa79174;hp=0620b32929d87c19ef5f79217094a9012f3df3ce;hb=f973cd867151f0aea61775936bfa2d69824f09e0;hpb=027b72114fc778535fae4a83c497dc47f71dedbd diff --git a/lnf b/lnf index 0620b32..8a9df12 100755 --- a/lnf +++ b/lnf @@ -3,39 +3,39 @@ # This program is under GPL v. 3 or later, see _lnf_existing_link() { - local target dest_file dest_dir - target="$1" - dest_file="$2" - dest_dir="$3" - if [[ -L $dest_file ]]; then - if [[ $(readlink $dest_file) == "$target" ]]; then - # Leave the link in place, but make sure it's - # owner & group is as if we created it. - # links all get 777 perms, so - # we already know that is right. - - # test for setgid. - if [[ $(stat -L -c%a "$dest_dir") == 2??? ]]; then - grp=$(stat -L -c%g "$dest_dir") || return $? - else - grp=$(id -g) || return $? - fi - if [[ $EUID == 0 && $(stat -c%u "$dest_file") != 0 ]]; then - chown -h 0:$grp "$dest_file" || return $? - elif [[ $(stat -c%g "$dest_file") != "$grp" ]]; then - chgrp -h $grp "$dest_file" || return $? - fi - do_exit=true - return 0 - fi - to_remove+=("$dest_file") - elif [[ -e $dest_file ]]; then - to_remove+=("$dest_file") + local target dest_file dest_dir + target="$1" + dest_file="$2" + dest_dir="$3" + if [[ -L $dest_file ]]; then + if [[ $(readlink $dest_file) == "$target" ]]; then + # Leave the link in place, but make sure it's + # owner & group is as if we created it. + # links all get 777 perms, so + # we already know that is right. + + # test for setgid. + if [[ $(stat -L -c%a "$dest_dir") == 2??? ]]; then + grp=$(stat -L -c%g "$dest_dir") || return $? + else + grp=$(id -g) || return $? + fi + if [[ $EUID == 0 && $(stat -c%u "$dest_file") != 0 ]]; then + chown -h 0:$grp "$dest_file" || return $? + elif [[ $(stat -c%g "$dest_file") != "$grp" ]]; then + chgrp -h $grp "$dest_file" || return $? + fi + do_exit=true + return 0 fi - to_link+=("$target") + to_remove+=("$dest_file") + elif [[ -e $dest_file ]]; then + to_remove+=("$dest_file") + fi + to_link+=("$target") } lnf() { - local help="Usage: + local help="Usage: lnf [OPTIONS] -T TARGET LINK_NAME (1st form) lnf [OPTIONS] TARGET (2nd form) lnf [OPTIONS] TARGET... DIRECTORY (3rd form) @@ -57,128 +57,128 @@ links to each TARGET in DIRECTORY. " - local temp nodir - local verbose=false - local dry_run=false - local do_exit=false - temp=$(getopt -l help,dry-run,verbose hnTv "$@") || usage 1 - eval set -- "$temp" - while true; do - case $1 in - -n|--dry-run) dry_run=true; verbose=true; shift ;; - -T) nodir=-T; shift ;; - -v|--verbose) verbose=true; shift ;; - -h|--help) echo "$help"; return 0 ;; - --) shift; break ;; - *) echo "$0: Internal error! unexpected args: $*" ; exit 1 ;; - esac - done - - if (( $# == 0 )); then - echo "$help" - return 1 - fi - - if [[ $nodir ]]; then - if (( $# != 2 )); then - echo "lnf: error: expected 2 arguments with -T flag. Got $#" - return 1 - fi + local temp nodir + local verbose=false + local dry_run=false + local do_exit=false + temp=$(getopt -l help,dry-run,verbose hnTv "$@") || usage 1 + eval set -- "$temp" + while true; do + case $1 in + -n|--dry-run) dry_run=true; verbose=true; shift ;; + -T) nodir=-T; shift ;; + -v|--verbose) verbose=true; shift ;; + -h|--help) echo "$help"; return 0 ;; + --) shift; break ;; + *) echo "$0: Internal error! unexpected args: $*" ; exit 1 ;; + esac + done + + if (( $# == 0 )); then + echo "$help" + return 1 + fi + + if [[ $nodir ]]; then + if (( $# != 2 )); then + echo "lnf: error: expected 2 arguments with -T flag. Got $#" + return 1 fi + fi - local reset_extglob=false - ! shopt extglob >/dev/null && reset_extglob=true - shopt -s extglob - - - local -a to_remove to_link - local x ret prefix dest_dir grp target - local mkdir=false - - if [[ $nodir ]]; then - dest_file="$2" - dest_dir="$(dirname "$dest_file")" - _lnf_existing_link "$1" "$dest_file" "$dest_dir" || return $? - if $do_exit; then return 0; fi - if [[ ! -d $dest_dir ]]; then - mkdir=true - if [[ -e $dest_dir || -L $dest_dir ]]; then - to_remove+=("$dest_dir") - fi - fi - to_link+=("$dest_file") - elif (( $# >= 2 )); then - dest_dir="${!#}" - if [[ -d $dest_dir ]]; then - prefix="$dest_dir" # last arg - for target in "${@:1:$(( $# - 1 ))}"; do # all but last arg - # Remove 1 or more trailing slashes, using. - dest_file="${target%%+(/)}" - # remove any leading directory components, add prefix - dest_file="$prefix/${target##*/}" - _lnf_existing_link "$target" "$dest_file" "$dest_dir" - done - else - to_link+=("${@:1:$(( $# - 1 ))}") - mkdir=true - fi - if (( ${#to_link[@]} == 0 )); then - return 0 - fi - to_link+=("$dest_dir") - elif [[ $# -eq 1 ]]; then - dest_file="${1##*/}" - _lnf_existing_link "$1" "$dest_file" . || return $? - if $do_exit; then return 0; fi - fi - if (( ${#to_remove[@]} >= 1 )); then - if type -P trash-put >/dev/null; then - if $verbose; then - echo "lnf: trash-put -- ${to_remove[*]}" - fi - if ! $dry_run; then - trash-put -- "${to_remove[@]}" || ret=$? - fi - # 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 "lnf: using rm -rf to overcome cross filesystem trash-put limitation" - rm -rf -- "${to_remove[@]}" || return $? - elif [[ $ret == 74 ]]; then - echo "lnf: using rm -rf to overcome empty file & hardlink trash-put limitation" - rm -rf -- "${to_remove[@]}" - elif [[ $ret && $ret != 0 ]]; then - return $x - fi - else - if $verbose; then - echo "lnf: rm -rf -- ${to_remove[*]}" - fi - if ! $dry_run; then - rm -rf -- "${to_remove[@]}" - fi - fi - fi + local reset_extglob=false + ! shopt extglob >/dev/null && reset_extglob=true + shopt -s extglob - $reset_extglob && shopt -u extglob - if $mkdir; then - if $verbose; then - echo "lnf: mkdir -p $dest_dir" - fi + local -a to_remove to_link + local x ret prefix dest_dir grp target + local mkdir=false - if ! $dry_run && ! mkdir -p "$dest_dir"; then - echo "lnf error: failed to make directory $dest_dir" - return 1 - fi + if [[ $nodir ]]; then + dest_file="$2" + dest_dir="$(dirname "$dest_file")" + _lnf_existing_link "$1" "$dest_file" "$dest_dir" || return $? + if $do_exit; then return 0; fi + if [[ ! -d $dest_dir ]]; then + mkdir=true + if [[ -e $dest_dir || -L $dest_dir ]]; then + to_remove+=("$dest_dir") + fi fi + to_link+=("$dest_file") + elif (( $# >= 2 )); then + dest_dir="${!#}" + if [[ -d $dest_dir ]]; then + prefix="$dest_dir" # last arg + for target in "${@:1:$(( $# - 1 ))}"; do # all but last arg + # Remove 1 or more trailing slashes, using. + dest_file="${target%%+(/)}" + # remove any leading directory components, add prefix + dest_file="$prefix/${target##*/}" + _lnf_existing_link "$target" "$dest_file" "$dest_dir" + done + else + to_link+=("${@:1:$(( $# - 1 ))}") + mkdir=true + fi + if (( ${#to_link[@]} == 0 )); then + return 0 + fi + to_link+=("$dest_dir") + elif [[ $# -eq 1 ]]; then + dest_file="${1##*/}" + _lnf_existing_link "$1" "$dest_file" . || return $? + if $do_exit; then return 0; fi + fi + if (( ${#to_remove[@]} >= 1 )); then + if type -P trash-put >/dev/null; then + if $verbose; then + echo "lnf: trash-put -- ${to_remove[*]}" + fi + if ! $dry_run; then + trash-put -- "${to_remove[@]}" || ret=$? + fi + # 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 "lnf: using rm -rf to overcome cross filesystem trash-put limitation" + rm -rf -- "${to_remove[@]}" || return $? + elif [[ $ret == 74 ]]; then + echo "lnf: using rm -rf to overcome empty file & hardlink trash-put limitation" + rm -rf -- "${to_remove[@]}" + elif [[ $ret && $ret != 0 ]]; then + return $x + fi + else + if $verbose; then + echo "lnf: rm -rf -- ${to_remove[*]}" + fi + if ! $dry_run; then + rm -rf -- "${to_remove[@]}" + fi + fi + fi + $reset_extglob && shopt -u extglob + + if $mkdir; then if $verbose; then - echo "lnf: ln -s $nodir -- ${to_link[*]}" + echo "lnf: mkdir -p $dest_dir" fi - if ! $dry_run; then - ln -s $nodir -- "${to_link[@]}" + + if ! $dry_run && ! mkdir -p "$dest_dir"; then + echo "lnf error: failed to make directory $dest_dir" + return 1 fi + fi + + if $verbose; then + echo "lnf: ln -s $nodir -- ${to_link[*]}" + fi + if ! $dry_run; then + ln -s $nodir -- "${to_link[@]}" + fi } lnf "$@"