#!/bin/sh
# findls is a simplified and enhanced version of 'find'
# findlsi should be a smylink to 'findls'
#
# Copyright (C) 2012-2013 Ken Jackson <linux at kenjackson.us>
# License: GNU General Public License version 3

VERSION='7-5-13'
error_exit() { echo -e "ERROR: $*" 1>&2; exit 1; }
usage()
{
    cat <<EOF
A more user-friendly but limited 'find' command.

Usage:
    ${0##*/} [switches] [dir ...] [file-glob ...]

Switches:
    -d N          Only search to a depth of N (1 for files in current dir)
    -dirs         Only find directories
    -g  PATTERN   Use grep on each file found instead of listing it
    -gl PATTERN   List only files which contain the grep pattern
    -i            Insensitive case filenames (default with findlsi)
    -ls           Display results like 'ls -o' instead of <size> <date> <path>
    -n|--dry-run  Don't do anything, just show what would be done
    -p PATH       Prune PATH, may contain glob wildcards (* ?)
    -q            Quiet--don't display errors (such as permission denied)
    -V|--version  Print the version (date) and exit

Arguments:
    dir           Directories to search (default: .)
    file-glob     Filenames optionally containing glob wildcards (* ?)
EOF
}

case "$(uname -s)" in
    *BSD|Linux|CYGWIN*) PLUS='+'     ;;
    *)                  PLUS='\\\\;' ;; # QNX, etc.
esac
EVAL=eval  DIR= TYPE= DEPTH= PRUNE= NAME= GREP= GREPOPT= QUIET=
MULTIPLE_NAMES=false
test "${0%i}i" = "$0"  &&  I=i  ||  I=
EXEC="-printf \"%8s %TD %p\\\\n\""

while test $# -gt 0; do
    case "$1" in
        -n|-nn|--dry-run) EVAL='echo -e #'  ;;
        -h|-help|--help)  usage;  exit      ;;
        -i)  I=i                            ;;
        -dirs|--dirs|-dir)  TYPE=' -type d' ;;
        -d*) case "$1" in
                 -d) shift; DEPTH="$1"      ;;
                 *)         DEPTH="${1#-d}" ;;
             esac
             case "$DEPTH" in
                 ""|*[!0-9]*)
                     error_exit "-d takes a numeric argument, maxdepth" ;;
             esac
             ;;
        -e)  test -n "$GREP"  &&  error_exit "-e can't be used with -g"
             EXEC="-print"
             ;;
        -g*) test -n "$GREP"  &&  error_exit "-g can only be used once"
             test "x$1" != x-g  &&  GREPOPT="-${1#-g}"
             shift
             GREP="$1"
             test -z "$GREP"  &&  error_exit "-g takes an argument for grep"
             ;;
        -links|--links) TYPE=' -type l'                  ;;
        -ls) test -n "$GREP"  &&  error_exit "-ls can't be used with -g"
             case "$(uname -s)" in      # BSD doesn't understand "ls -o"
                 *BSD)          EXEC="-exec ls -ld {} +" ;;
                 Linux|CYGWIN*) EXEC="-exec ls -od {} +" ;;
                 *)             EXEC="-exec ls -ld {} \\\\;" ;; # QNX, etc.
             esac
             ;;
        -p)  shift
             test -z "$1"  &&  error_exit "-p takes a path argument to prune"
             PRUNE="$PRUNE -path '$1' -prune -o"      ;;
        -p*) PRUNE="$PRUNE -path '${1#-p}' -prune -o" ;;
        -q)  QUIET='2>/dev/null'                      ;;
        -V|--vers*)   echo "${0##*/} v$VERSION"; exit ;;
        -*)  error_exit "Unknown switch \"$1\""       ;;
        "")  ;;
        *)  arg="$1"
            if [ -d "$arg" ]; then
                DIR="$DIR '${arg%/}/'"  # Symbolic links don't work without '/'
            elif [ "${arg#*/}" != "$arg" ]; then
                error_exit "\"$arg\" has a slash but is not a directory"
            elif [ -z "$NAME" ]; then
                NAME="-${I}name '$arg'"
            else
                NAME="$NAME -o -${I}name '$arg'"
                MULTIPLE_NAMES=true
            fi
            ;;
    esac
    shift
done

# Adjust variables

test -n "$GREP"  &&  EXEC="-type f -exec grep $GREPOPT '$GREP' '{}' '$PLUS'"
test -n "$DEPTH" &&  DEPTH=" -maxdepth $DEPTH"
$MULTIPLE_NAMES  &&  NAME="'(' $NAME ')'"

# Do the find

$EVAL "find ${DIR:-.}$DEPTH$TYPE$PRUNE $NAME $EXEC $QUIET"
