#!/usr/bin/env bash
# Desc: Applies chmod to a segment of a directory branch of the
# filesystem tree. Changes applied to all directories starting with
# TIP and ending with (but not including) TRUNK.

# Usage: chmoddirbr [path TRUNK] [path TIP]
# Input: arg1: TRUNK path
#        arg2: TIP path
#        arg3+ arguments to be passed on to chmod
# Output: None.
# Depends: GNU Coreutils 8.30 (chmod), Bash 5.0.3

# Example: chmoddirbr /tmp /tmp/1/2/3/4/5/6 o+rx
#   This runs `chmod o+rx /tmp/1/2/3/4/5/6`
#             `chmod o+rx /tmp/1/2/3/4/5`
#             `chmod o+rx /tmp/1/2/3/4`
#             ...
#             `chmod o+rx /tmp/1`

yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
try() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
main() {
    declare -a chmod_args
    declare -a dirs_branch
    
    arg1="$1"; shift;
    arg2="$1"; shift;
    chmod_args=("$@");

    #yell "DEBUG:arg1:$arg1";
    #yell "DEBUG:arg2:$arg2";
    #yell "DEBUG:chmod_args:${chmod_args[@]}";

    # Check input dirs
    if [[ -d $arg1 ]]; then
	dir_trunk="$(readlink -f "$arg1")"; # use full path
	#yell "DEBUG:dir_trunk:$dir_trunk";
    else
	die "ERROR:TRUNK is not a dir:$arg1";
    fi;
    if [[ -d $arg2 ]]; then
	dir_tip="$(readlink -f "$arg2")"; # use full path
	#yell "DEBUG:dir_tip  :$dir_tip";
    else
	die "ERROR:TIP is not a dir:$arg2";
    fi;

    # Check if $dir_trunk points to $dir_tip
    if [[ $dir_tip =~ ^"$dir_trunk" ]]; then
	:
	#yell "DEBUG:Tip is within trunk.";
    else
	yell "NOTE:dir_trunk:$dir_trunk";
	yell "NOTE:dir_tip  :$dir_tip";
	die "ERROR:Tip is not within trunk.";
    fi;

    # Assemble dirs_branch array
    ## Note: dirs_branch will not include $dir_trunk
    point="$dir_tip";
    while [[ $point != $dir_trunk ]]; do
	#yell "DEBUG:start point    :$point";
	#yell "DEBUG:start dir_trunk:$dir_trunk";
	dirs_branch+=("$point");

	# Walk up branch
	point="$(dirname "$point")";
	#yell "DEBUG:end   point    :$point";
	#yell "DEBUG:end   dir_trunk:$dir_trunk";
	#yell ""; # debug
    done;
    unset point;
    #yell "DEBUG:${dirs_branch[@]}";

    # Perform action
    n=0;
    for dir in "${dirs_branch[@]}"; do
	yell "STATUS:$((n+1)):Running: chmod $chmod_args $dir";
	try chmod "$chmod_args" "$dir" && ((n++));
    done;
    yell "STATUS:Finished. Ran chmod $((n)) times.";
} # main program

main "$@";

# Author: Steven Baltakatei Sandoval
# License: GPLv3+