#!/bin/bash
#
# shop - Show Permissions at all levels of a given path
#
#        With `tree(1)` you start at a trunk and show all leaves that
#        originate from that trunk, optionally showing permissions.
#        `shop` starts at a leaf and shows the permissions back to a
#        trunk.
#
# CREATED:  2009-09-03 17:30
# MODIFIED: 2009-11-17 11:05

_NAME=$(basename $0)
_VERSION=1.0

# DEFAULTS
unset octal               # use text mode instead of octal mode
level=-1                  # traverse all the way up to /
trunk='/'                 # traverse all the way up to /
pad=17                    # padding for pathname

usage() {
    cat <<EOT
Usage: $_NAME [-L N] [-o] [-t PATH] [PATH1..]

Options:
  -L, --level N      traverse N levels up the tree
  -o, --octal        show octal mode instead of human readable mode 
  -t, --trunk PATH   only traverse up to PATH instead of / (root)
                       - takes precedence over --level
  -p, --pad N        allow USER:GROUP N characters before directory name
                       default: 17

  -h, --help         show this message
      --version      show version info
EOT
}

_shop() {
    [ $octal ] && stat_str="%a" || stat_str="%A"

    stats=( $(stat -c "$stat_str %U:%G %n" "$1") )

    if [ $octal ]; then
        # `stat -c "%a"` only returns a 4 digit mode when the first digit is 
        # nonzero, yet `stat` always returns a 4 digit mode.  how annoying...
        [ ${#stats} -eq 3 ] && echo -n "0"
    fi    

    printf "%s %-${pad}s ${stats[@]:2}\n" "${stats[@]:0:2}"
}

main() {
    arg="$1"
    if [ -z "$arg" ]; then
        # user didn't supply an arg, use current working dir
        arg="$PWD"
    fi

    until [ -z "$arg" ]; do
        if ! [ -a "$arg" ]; then
             echo "error: $arg does not exist"
             return 1
        fi

        # if $arg is a directory, prime the directory stack, else use 
        # the parent dir of $arg to prime the stack
        if [ -d "$arg" ]; then
            cd "$arg" || return 1
            unset file_arg
        else
            cd $(dirname -- "$arg") || return 1
            file_arg=1
            let "level -=1"
        fi

        start_dir=$PWD

        # populate directory stack with $level levels on the path to
        # the $trunk
        while [ "$PWD" != "$trunk" ]; do
            if [ $level -gt 0 ] || [ $level -lt 0 ]; then
                let "level -= 1"
                pushd .. &> /dev/null 
            elif [ $level -eq 0 ]; then
                break;
            fi
        done

        # display the permissions for each level
        while [ "$PWD" != "$start_dir" ]; do
            _shop "$PWD"
            popd &> /dev/null
        done
        _shop "$PWD"

        # leaf was a file, run _shop on this file as well
        if [ $file_arg ]; then
            _shop "${PWD}/$(basename -- "$arg")"
        else
            cd .. || return 1
        fi

        # user passed multiple leafs, separate output for each leaf
        if [ $# -gt 1 ]; then
            echo
        fi

        shift
        arg="$1"
    done
}

declare -a args
until [ -z "$1" ]; do
    case "$1" in
        -L|--level) level="$2"
                    shift 2
                    ;;

        -o|--octal) octal=1
                    shift
                    ;;

        -t|--trunk) trunk="$(readlink -f $2)"
                    shift 2
                    ;;

        -p|--pad) pad="$2"
                  shift 2
                  ;;

        -h|--help) usage
                   exit
                   ;;

        --version) echo "$_NAME v$_VERSION"
                   exit
                   ;;

        --) shift
            args=( "${args[@]}" "$@" )
            break
            ;;

        -*) echo -e "$_NAME: unknown option: $1\n"
            usage
            exit
            ;;

        *) args[${#args[*]}]="$1"
           shift
           ;;
    esac
done

main "${args[@]}"