license change
[lnf] / lnf
diff --git a/lnf b/lnf
index 3bc51523444ee9e70fadc523e088979fc69d0f20..28f77ee7e8aa0d69cf650e1537d2c054ff3ff8d0 100755 (executable)
--- a/lnf
+++ b/lnf
@@ -1,38 +1,60 @@
 #!/bin/bash
 #!/bin/bash
-# Copyright (C) 2014-2016 Ian Kelling
-# This program is under GPL v. 3 or later, see <http://www.gnu.org/licenses/>
+# I, Ian Kelling, follow the GNU license recommendations at
+# https://www.gnu.org/licenses/license-recommendations.en.html. They
+# recommend that small programs, < 300 lines, be licensed under the
+# Apache License 2.0. This file contains or is part of one or more small
+# programs. If a small program grows beyond 300 lines, I plan to switch
+# its license to GPL.
+
+# Copyright 2024 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 
 _lnf_existing_link() {
 
 _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
-            # ownership is right.
-            # already exists. links all get 777 perms, so
-            # we dun have to mess with that.
-            if [[ $(stat -L -c%a "$dest_dir") == 2* ]]; then
-                grp=$(stat -L -c%g "$dest_dir")
-            else
-                grp=$(id -g)
-            fi
-            if [[ $EUID == 0 && $(stat -c%u "$dest_file") != 0 ]]; then
-                chown 0:$grp "$dest_file"
-            elif [[ $(stat -c%g "$dest_file") != "$grp" ]]; then
-                chgrp $grp "$dest_file"
-            fi
-            return 1
-        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
     fi
-    to_link+=("$target")
+    to_remove+=("$dest_file")
+  elif [[ -e $dest_file ]]; then
+    to_remove+=("$dest_file")
+  fi
+  to_link+=("$target")
 }
 lnf() {
 }
 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)
        lnf [OPTIONS] -T TARGET LINK_NAME     (1st form)
        lnf [OPTIONS] TARGET                  (2nd form)
        lnf [OPTIONS] TARGET... DIRECTORY     (3rd form)
@@ -40,9 +62,8 @@ Create symlinks forcefully
 
 If the link already exists, make it's ownership be the same as if it was
 newly created (only chown if we are root).  Removes existing files using
 
 If the link already exists, make it's ownership be the same as if it was
 newly created (only chown if we are root).  Removes existing files using
-trash-put or rm -rf if it is not available, or if trash-put fails due to a
-limitation such as a cross-filesystem link. Create directory of link if
-needed. Slightly more restrictive arguments than ln.
+rm -rf. Create directory of link 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
 
 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
@@ -54,126 +75,107 @@ links to each TARGET in DIRECTORY.
 "
 
 
 "
 
 
-    local temp
-    local verbose=false
-    local dry_run=false
-    local nodir
-    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
+  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 0
-        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 0
-    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[@]}"
-            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 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
     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 $verbose; then
     if $verbose; then
-        echo "lnf: ln -s $nodir -- ${to_link[*]}"
+      echo "lnf: rm -rf -- ${to_remove[*]}"
     fi
     if ! $dry_run; then
     fi
     if ! $dry_run; then
-        ln -s $nodir -- "${to_link[@]}"
+      rm -rf -- "${to_remove[@]}"
+    fi
+  fi
+
+  $reset_extglob && shopt -u extglob
+
+  if $mkdir; then
+    if $verbose; then
+      echo "lnf: mkdir -p $dest_dir"
+    fi
+
+    if ! $dry_run && ! mkdir -p "$dest_dir"; then
+      echo "lnf error: failed to make directory $dest_dir"
+      return 1
     fi
     fi
+  fi
+
+  if $verbose; then
+    echo "lnf: ln -s $nodir -- ${to_link[*]}"
+  fi
+  if ! $dry_run; then
+    ln -s $nodir -- "${to_link[@]}"
+  fi
 }
 lnf "$@"
 }
 lnf "$@"