#!/usr/bin/env bash
#
# See "Customisable variables" to add or prioritise your favourite apps
# Install this on your $PATH and link to 'x' with 'ln v x' or 'ln -s v x'
#
# Copyright (C) 2008-2013 Bob Hepple
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

# http://bhepple.freeshell.org

verbose_out() {
    [ "$VERBOSE" ] && echo "$PROG: $@" >>$TRACE
}

out() {
    echo "$PROG: $@" >>$TRACE
}

filelist() {
    for A in "${FILE[@]}"; do
        echo -n "\"$A\" "
    done
}

###########################################
# ARG: category candidate1 candidate2 ... # 
# Find an executable for a category       #
###########################################
check_exec() {
    CAT="$1"
    shift

    verbose_out "check_exec: seeking category \"$CAT\"" 

    # get next candidate - if we get 'xdf -o1 -o2' then re-assemble the 
    # arguments, strip off the '' and test on arg1 only
    for P in ${!CAT} "$@" false; do
        C="$P"
        case "$P" in
            "") continue;;
            \'*) 
                SAVE="$P"
                continue
                ;;
        esac

        if [ "$SAVE" ]; then
            SAVE="$SAVE $P"
            case "$P" in
                *\') 
                    P=$(echo "$SAVE" |tr -d "'")
                    C="$P"
                    P=$(echo "$P" |awk '{print $1;}')
                    SAVE=""
                    ;;
                *)
                    continue
                    ;;
            esac
        fi
            
        if type $P >/dev/null 2>&1; then
            break
        fi
    done

    if [ "$C" = "false" ]; then
        verbose_out "check_exec: nothing is available for \"$CAT\": tried $@"
        exit 1
    fi
    verbose_out "check_exec: \"$CAT\" is \"$C\"" 
    echo $C
    exit
}

check_suffix_for_x_viewer() {
###################################################
# handle files on the basis of the suffix: X apps #
###################################################
    verbose_out "check_suffix_for_x_viewer:"
    case "${FILE[0]}" in
        *.me) 
            TMPFILE="/tmp/tmp$$.ps"
            VIEWER=$(check_exec $X_PS_VIEWERS)
            [ "$VIEWER" ] && {
                COM="tbl \"${FILE[0]}\"| groff -Tps -c -me > \"$TMPFILE\"; $VIEWER \"$TMPFILE\""
                trap '/bin/rm -f "$TMPFILE"' EXIT
            }
            ;;

        *.ps) # nope - 'file -i' says application/x-awk!! on fedora-8
            COM=$(check_exec $X_PS_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)" || {
                VIEWER=$(check_exec $X_PDF_VIEWERS)
                [ "$VIEWER" ] && type ps2pdf >/dev/null 2>&1 && {
                    case "$VIEWER" in
                        xpdf*) XPDFGEOM=$(xdpyinfo |awk '/dimensions/ {print $2}'|awk -Fx '{printf("%dx%d+0+0", 0.95*$1, $2-25)}')
                            ;;
                    esac
                    TMPFILE="/tmp/tmp$$.pdf"
                    COM="ps2pdf \"${FILE[0]}\" \"$TMPFILE\"; $VIEWER \"$TMPFILE\""
                    trap '/bin/rm -f "$TMPFILE"' EXIT
                }
            }
            ;;

        *.pdf )
            COM=$(check_exec $X_PDF_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            case "$COM" in
                xpdf*)
                    XPDFGEOM=$(xdpyinfo |awk '/dimensions/ {print $2}'|awk -Fx '{printf("%dx%d+0+0", 0.95*$1, $2-25)}')
                    ;;
            esac
            ;;

        *.chm)
            COM=$(check_exec $X_CHM_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *.epub)
            COM=$(check_exec $X_EPUB_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *.mobi|*.lit|*.prc|*.odt|*.cbr|*.cbz|*.rtf|*.lrs)
            COM=$(check_exec $X_MOBI_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;
   
        *.[0-9] | *.n | *.man | *.[0-9].gz | *.n.gz | *.man.gz) 
            COM=$(check_exec $X_MANPAGE_VIEWERS)
            [ "$COM" ] && COM="$COM \"$(fqn ${FILE[0]})\""
            ;;

        *.html | *.htm | *.xml)
            # sometimes file -i doesn't recognise these - eg if they
            # have CR characters, so check for them here:
            COM=$(check_exec $X_HTML_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *.odt | *.sdw )
            COM=$(check_exec $X_OODOC_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *.doc )
            COM=$(check_exec $X_DOC_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *.xls )
            COM=$(check_exec $X_SSHEET_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *.ods )
            COM=$(check_exec $X_OOSSHEET_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *.ppt )
            COM=$(check_exec $X_PRES_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *.odp )
            COM=$(check_exec $X_OOPRES_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        # file messes up .mp3 recognition too!
        *.mp3 | *.ogg | *.oga | *.flac | *.mp2 | *.wav | *.m4a)
            COM=$(check_exec $X_AUDIO_PLAYERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *.avi|*.mov|*.mpeg|*.mpg|*.vob|*.wma|*.ram|*.mp4|*.wmv|*.mkv|*.m4v|*.ogv)
            COM=$(check_exec $X_VIDEO_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;
    esac

    verbose_out "check_suffix_for_x_viewer: COM=$COM"
}

check_suffix_for_console_viewer() {
    #########################################################
    # handle files on the basis of the suffix: console apps #
    #########################################################

    verbose_out "check_suffix_for_console_viewer"
    # it only makes sense to process a single file in most of these cases:
    COM=""
    case "${FILE[0]}" in
        *.cpio.bz2 ) COM="$BUNZIP2 \"${FILE[0]}\" | $LIST_CPIO | $C_PAGER" ;;
        *.cpio.gz ) COM="$BUNZIP2 \"${FILE[0]}\" | $LIST_CPIO | $C_PAGER" ;;
        *.cpio ) COM="cat \"${FILE[0]}\" | $LIST_CPIO | $C_PAGER";;
        *.tar.bz2 | *.tbz2 | *.tbz) COM="$BUNZIP2 \"${FILE[0]}\" |$LIST_TAR | $C_PAGER" ;;
        *.tar.gz | *.tgz | *.tar.[zZ] ) COM="$GUNZIP \"${FILE[0]}\" | $LIST_TAR | $C_PAGER" ;;
        *.tar | *.gem ) COM="cat \"${FILE[0]}\" | $LIST_TAR | $C_PAGER" ;;
        *.[0-9].gz | *.n.gz | *.man.gz ) COM="$GUNZIP \"${FILE[0]}\"| tbl| nroff -c -man| col | $C_PAGER" ;;
        *.[0-9] | *.n | *.man ) COM="tbl \"${FILE[0]}\"| nroff -c -man| col | $C_PAGER" ;;
        *.me) COM="tbl \"${FILE[0]}\"| nroff -c -me |col  | $C_PAGER" ;;
        *.gz | *.[zZ] ) COM="$GUNZIP \"${FILE[0]}\" | $C_PAGER" ;;
        *.bz2 ) COM="$BUNZIP2 \"${FILE[0]}\" | $C_PAGER" ;;
        *.rpm ) COM="$LIST_RPM \"${FILE[0]}\" | $C_PAGER" ;;
        *.zip ) COM="$LIST_ZIP \"${FILE[0]}\" | $C_PAGER" ;;
        *.7z )  COM="$LIST_7Z \"${FILE[0]}\" | $C_PAGER" ;;
        *.jar ) COM="$LIST_JAR \"${FILE[0]}\" | $C_PAGER" ;;
        *.rar ) COM="$LIST_RAR \"${FILE[0]}\" | $C_PAGER" ;;
        *.deb ) COM="($LIST_DEB_INFO \"${FILE[0]}\"; $LIST_DEB_CONTENTS \"${FILE[0]}\") | $C_PAGER" ;;

        *.pkg ) COM="pkginfo -d \"${FILE[0]}\" -l | $C_PAGER" ;; # solaris, sco 
        *.depot )  # HPUX
            FQN=$(fqn "\"${FILE[0]}\"") || exit 1
            COM="swlist -v -s $FQN && swlist -l file -s $FQN | $C_PAGER" 
            ;;
        *.bff) # AIX
            FQN=$(fqn "\"${FILE[0]}\"") || exit 1
            D=$(dirname $FQN) 
            F=$(basename $FQN)
            COM="installp -iq -d $D -f $F | $C_PAGER"
            ;;

        *.crt) # probably a cert
            FMT=
            if ! grep 'BEGIN CERT' "${FILE[0]}" &> /dev/null ; then
                # try DER format
                FMT="-inform DER"
            fi
            COM="openssl x509 -text $FMT -in \"${FILE[0]}\" | $C_PAGER"
            ;;
            
        # 'file -i' thinks very simple .h files are
        # image/x-portable-bitmap, so trap them here: while we're at
        # it, we might as well trap other common text files here:
        *.[ch] | *.py | *.pl | *.sh | *.txt | [Mm]akefile*| *.html | *.htm | *.xml | *.log) 
            COM="$C_PAGER $(filelist)"
            ;;

    # file messes up .mp3 recognition too!
        *.mp3 | *.ogg | *.oga | *.flac | *.mp2 | *.wav | *.m4a) 
            COM=$(check_exec $C_AUDIO_PLAYERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *.avi | *.mov | *.mpeg | *.mpg | *.vob | *.wma | *.ram | *.mp4 | *.wmv | *.mkv|*.ogv)
            COM=$(check_exec $C_VIDEO_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;
    esac

    if [ "$COM" ]; then
        if [ "$RUN_IN_CONSOLE" ]; then
            COM="${RUN_IN_CONSOLE}'$COM'"
        fi
    fi
    verbose_out "check_suffix_for_console_viewer: COM=$COM"
}

check_mime_for_x_viewer() {
######################################################
# handle files according to what 'file' says: X apps #
######################################################

    TYPE=$(file -L -i -b "${FILE[0]}")
    verbose_out "check_mime_for_x_viewer: type=$TYPE"

    case "$TYPE" in
        image/jpeg | image/tiff | image/gif | image/x-xpm | image/*) 
            COM=$(check_exec $X_GRAPH_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        application/pdf)
            COM=$(check_exec $X_PDF_VIEWERS)
            case "$COM" in
                xpdf*)
                    XPDFGEOM=$(xdpyinfo |awk '/dimensions/ {print $2}'|awk -Fx '{printf("%dx%d+0+0", 0.95*$1, $2-25)}')
                    ;;
            esac
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        application/postscript)         
            COM=$(check_exec $X_PS_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        application/msword)             
            COM=$(check_exec $X_DOC_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        text/html)
            COM=$(check_exec $X_HTML_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        text/*|*shellscript|*awk|*perl)
            [ "$X_PAGER" ] && COM="cat \"${FILE[0]}\"|$X_PAGER"
            ;;

        audio/*)
            COM=$(check_exec $X_AUDIO_PLAYERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        video/*)
            COM=$(check_exec $X_VIDEO_VIEWERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *)  
            if [ -d "${FILE[0]}" ]; then
                if [ -d "${FILE[0]}/VIDEO_TS" -o -d "${FILE[0]}/video_ts" ]; then
                    COM=$(check_exec $X_DVD_VIEWERS)
                    [ "$COM" ] && COM="$COM$(filelist)"
                else
                    # file gives application/x-not-regular-file!
                    COM=$(check_exec $X_DIR_VIEWERS)
                fi
            fi
            ;;
    esac

#    if [ "$COM" ]; then
#       unset TERM # in case the child wants to invoke us!!
#    fi
    verbose_out "check_mime_for_x_viewer: COM=$COM"
}

check_mime_for_console_viewer() {
############################################################
# handle files according to what 'file' says: console apps #
############################################################

    TYPE=$(file -L -i -b "${FILE[0]}")
    verbose_out "check_mime_for_console_viewer: type = $TYPE"

    case "$TYPE" in
        image/jpeg | image/tiff | image/gif | image/x-xpm | image/*) 
            COM=$(check_exec $C_GRAPH_VIEWERS)
            ;;

        application/pdf) 
            COM=$(check_exec $C_PDF_VIEWERS)
            ;;

        application/postscript)         
            COM=$(check_exec $C_PS_VIEWERS)
            ;;

        application/msword)
            case "${FILE[0]}" in
                *.doc)
                    COM=$(check_exec $C_DOC_VIEWERS)
                    ;;
                *.xls | *.ppt)
                    # file thinks these are MSWORD too, but antiword
                    # can't cope with them
                    ;;
            esac
            ;;

        application/x-sharedlib*|application/x-object*)
            COM=$(check_exec $C_OBJ_VIEWERS)
            ;;

        text/html) 
            COM=$(check_exec $C_HTML_VIEWERS)
            ;;

        text/*|*shellscript|*awk|*perl)
            COM=$C_PAGER
            ;;

        audio/*)
            COM=$(check_exec $C_AUDIO_PLAYERS)
            ;;

        video/*)
            COM=$(check_exec $C_VIDEO_VIEWERS)
            ;;

        *)
            if [ -d "${FILE[0]}" ]; then
                # file gives application/x-not-regular-file!
                COM=$(check_exec $C_DIR_VIEWERS)
            fi
    esac

    if [ "$COM" ]; then
        if [ "$COM" != "$C_PAGER" ]; then
            COM="$COM $(filelist)|$C_PAGER"
        else
            COM="$COM $(filelist)"
        fi
        if [ "$RUN_IN_CONSOLE" ]; then
            COM="${RUN_IN_CONSOLE} '$COM'"
        fi
    fi
    verbose_out "check_mime_for_console_viewer: COM=$COM"
}

check_suffix_for_x_editor() {
###################################################
# handle files on the basis of the suffix: X apps #
###################################################
    verbose_out "check_suffix_for_x_editor:"
    case "${FILE[0]}" in
        *.html | *.htm | *.xml)
            # sometimes file -i doesn't recognise these - eg if they
            # have CR characters, so check for them here:
            COM=$(check_exec $X_HTML_EDITORS)
            ;;
        *.odt | *.sdw )
            COM=$(check_exec $X_OODOC_EDITORS)
            ;;
        *.doc )
            COM=$(check_exec $X_DOC_EDITORS)
            ;;
        *.xls )
            COM=$(check_exec $X_SSHEET_EDITORS)
            ;;
        *.ppt | *.odp )
            COM=$(check_exec $X_PRES_EDITORS)
            ;;
        *.gjots*)
            COM=$(check_exec $X_GJOTS_EDITORS)
            ;;
        # file messes up .mp3 recognition too!
        *.mp3 | *.ogg | *.oga | *.flac | *.mp2 | *.wav | *.m4a)
            COM=$(check_exec $X_AUDIO_EDITORS)
            ;;
    esac

    if [ "$COM" ]; then
        #unset TERM # in case the child wants to invoke us!!
        COM="$COM $(filelist)"
    fi
    verbose_out "check_suffix_for_x_editor: COM=$COM"
}

check_suffix_for_console_editor() {
    #########################################################
    # handle files on the basis of the suffix: console apps #
    #########################################################

    verbose_out "check_suffix_for_console_editor"
    # it only makes sense to process a single file in most of these cases:
    COM=""
    case "${FILE[0]}" in
        *.cpio.bz2 ) COM="$BUNZIP2 \"${FILE[0]}\" | $UNPACK_CPIO 2>&1 | $C_PAGER" ;;
        *.cpio.gz ) COM="$BUNZIP2 \"${FILE[0]}\" | $UNPACK_CPIO 2>&1 | $C_PAGER" ;;
        *.cpio ) COM="cat \"${FILE[0]}\" | $UNPACK_CPIO 2>&1 | $C_PAGER";;
        *.tar.bz2 | *.tbz2 | *.tbz) COM="$BUNZIP2 \"${FILE[0]}\" |$UNPACK_TAR 2>&1 | $C_PAGER" ;;
        *.tar.gz | *.tgz | *.tar.[zZ] ) COM="$GUNZIP \"${FILE[0]}\" | $UNPACK_TAR 2>&1 | $C_PAGER" ;;
        *.tar ) COM="cat \"${FILE[0]}\" | $UNPACK_TAR 2>&1 | $C_PAGER" ;;
        *.[0-9].gz | *.n.gz | *.man.gz ) COM="$GUNZIP \"${FILE[0]}\"| tbl| nroff -c -man| col 2>&1 | $C_PAGER" ;;
        *.[0-9] | *.n | *.man ) COM="tbl \"${FILE[0]}\"| nroff -c -man| col 2>&1 | $C_PAGER" ;;
        *.gz | *.[zZ] ) COM="$GUNZIP \"${FILE[0]}\" 2>&1 | $C_PAGER" ;;
        *.bz2 ) COM="$BUNZIP2 \"${FILE[0]}\" 2>&1 | $C_PAGER" ;;
        *.rpm ) COM="$UNPACK_RPM \"${FILE[0]}\" 2>&1 | $C_PAGER" ;;
        *.zip ) COM="$UNPACK_ZIP \"${FILE[0]}\" 2>&1 | $C_PAGER" ;;
        *.7z ) COM="$UNPACK_7Z \"${FILE[0]}\" 2>&1 | $C_PAGER" ;;
        *.jar ) COM="$UNPACK_JAR \"${FILE[0]}\" 2>&1 | $C_PAGER" ;;
        *.rar ) COM="$UNPACK_RAR \"${FILE[0]}\" 2>&1 | $C_PAGER" ;;
        *.deb ) COM="($UNPACK_DEB \"${FILE[0]}\" 2>&1 | $C_PAGER" ;;

        *.pkg ) COM="pkginfoFIXME -d \"${FILE[0]}\" -l 2>&1 | $C_PAGER" ;; # solaris, sco 
        *.depot )  # HPUX
            FQN=$(fqn "\"${FILE[0]}\"") || exit 1
            COM="swinstallFIXME -v -s $FQN && swlist -l file -s $FQN 2>&1 | $C_PAGER" 
            ;;
        *.bff) # AIX
            FQN=$(fqn "\"${FILE[0]}\"") || exit 1
            D=$(dirname $FQN) 
            F=$(basename $FQN)
            COM="installpFIXME -iq -d $D -f $F 2>&1 | $C_PAGER"
            ;;
        
    # 'file -i' thinks very simple .h files are image/x-portable-bitmap, so trap them here:
    # while we're at it, we might as well trap other common text files here:
        *.[ch] | *.py | *.pl | *.sh | *.txt | [Mm]akefile*| *.html | *.htm | *.xml) 
            COM="$C_EDITOR $(filelist)"
            ;;
    esac

    if [ "$COM" ]; then
        if [ "$RUN_IN_CONSOLE" ]; then
            COM="${RUN_IN_CONSOLE}'$COM'"
        fi
    fi
    verbose_out "check_suffix_for_console_editor: COM=$COM"
}

check_mime_for_x_editor() {
######################################################
# handle files according to what 'file' says: X apps #
######################################################

    TYPE=$(file -L -i -b "${FILE[0]}")
    verbose_out "check_mime_for_x_editor: type=$TYPE"

    case "$TYPE" in
        image/jpeg | image/tiff | image/gif | image/x-xpm | image/*) 
            COM=$(check_exec $X_GRAPH_EDITORS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        application/pdf)
            COM=$(check_exec $X_PDF_EDITORS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        application/postscript)         
            COM=$(check_exec $X_PS_EDITORS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        application/msword)             
            COM=$(check_exec $X_DOC_EDITORS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        text/html)
            COM=$(check_exec $X_HTML_EDITORS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        text/*|*shellscript|*awk|*perl)
            COM="$X_EDITOR $(filelist)"
            ;;

        audio/*)
            COM=$(check_exec $X_AUDIO_PLAYERS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        video/*)
            COM=$(check_exec $X_VIDEO_EDITORS)
            [ "$COM" ] && COM="$COM $(filelist)"
            ;;

        *)  
            ;;
    esac

#    if [ "$COM" ]; then
#       unset TERM # in case the child wants to invoke us!!
#    fi
    verbose_out "check_mime_for_x_editor: COM=$COM"
}

check_mime_for_console_editor() {
############################################################
# handle files according to what 'file' says: console apps #
############################################################

    TYPE=$(file -L -i -b "${FILE[0]}")
    verbose_out "check_mime_for_console_editor: type=$TYPE"

    case "$TYPE" in
        image/jpeg | image/tiff | image/gif | image/x-xpm | image/*) 
            COM=$(check_exec $C_GRAPH_EDITORS)
            ;;

        application/pdf) 
            COM=$(check_exec $C_PDF_EDITORS)
            ;;

        application/postscript)         
            COM=$(check_exec $C_PS_EDITORS)
            ;;

        application/msword)
            case "${FILE[0]}" in
                *.doc)
                    COM=$(check_exec $C_DOC_EDITORS)
                    ;;
                *.xls | *.ppt)
                    # file thinks these are MSWORD too
                    ;;
            esac
            ;;

        text/html) 
            COM=$(check_exec $C_HTML_EDITORS)
            ;;

        text/*|*shellscript|*awk|*perl)
            COM=$C_EDITOR
            ;;

        audio/*)
            COM=$(check_exec $C_AUDIO_PLAYERS)
            ;;

        video/*)
            COM=$(check_exec $C_VIDEO_EDITORS)
            ;;

        *)
            if [ -d "${FILE[0]}" ]; then
                # file gives application/x-not-regular-file!
                COM=$(check_exec $C_DIR_EDITORS)
            fi
    esac

    if [ "$COM" ]; then
        if [ "$COM" != "$C_EDITOR" ]; then
            COM="$COM $(filelist) | $C_PAGER"
        else
            COM="$COM $(filelist)"
        fi
        if [ "$RUN_IN_CONSOLE" ]; then
            COM="${RUN_IN_CONSOLE} '$COM'"
        fi
    fi
    verbose_out "check_mime_for_console_editor: COM=$COM"
}

check_and_process_opts() {

    ARGS="
ARGP_DELETE=quiet
ARGP_VERSION=$VERSION
ARGP_PROG=$PROG
##############################################################   
#OPTIONS:
#name=default sname arg       type range           description
##############################################################   
USE_X=''      X     ''        b    '$DISPLAY'      prefer X instead of console
EDIT='$EDIT'  e     ''        b    ''              edit or exec instead of view (default if run as 'x')
ALL=''        a     ''        b    ''              view all media files under PWD (recursively)
##############################################################   
ARGP_ARGS=[--] $ARGUMENTS
ARGP_SHORT=$SHORT_DESC
ARGP_USAGE=$USAGE"

    exec 4>&1
    eval $(echo "$ARGS" | argp.sh "$@" 3>&1 1>&4 || echo exit $? )
    exec 4>&-

    export PREFER_X=$USE_X

    NEW_ARGS=( "$@" )
    return 0
}

view_trace() {
    [ -s "$TRACE" ] && {
        TRACE_VIEWER="$C_PAGER $TRACE"
        [ "$RUN_IN_CONSOLE" ] && TRACE_VIEWER="$RUN_IN_CONSOLE '$TRACE_VIEWER'"
        [ "$PREFER_X" -a "$X_PAGER" ] && TRACE_VIEWER="$X_PAGER < $TRACE"
        eval $TRACE_VIEWER
    }
}

##########################
#         M A I N        #
##########################

PROG=$(basename $0)
VERSION="1.3"
COM=""
TRACE="/tmp/v.$$"
trap 'view_trace; rm -f $TRACE' EXIT

USAGE="\
View a file using an application appropriate to its type depending on \
whether you're running from a console or under X. Prefer console apps \
over X.

Converts M$ filenames to UNIX if the filename contains \\ or starts C:.

Does not rely on lesspipe.sh

Passes all files to the viewer if it makes sense to do so. Also, only \
the first file is inspected to decide how to view/edit/exec.

To force a particular viewer eg:
C_PAGER=more v file
#X_PAGER=\"'gxmessage -file -'\" v -X file"
X_PAGER="'zenity --text-info --width 800 --height 800 --filename /dev/stdin'"
SHORT_DESC="File viewer. "
ARGUMENTS="file(s)"

##########################
# Customisable variables #
##########################

LIST_TAR="tar tf -"
LIST_CPIO="cpio -tv"
BUNZIP2="bunzip2 -c"
GUNZIP="gunzip -c"
LIST_RAR="unrar l"
LIST_RPM="rpm -qpi "
LIST_JAR="jar -tf"
LIST_ZIP="unzip -l"
LIST_7Z="7za l "
LIST_DEB_INFO="dpkg --info"
LIST_DEB_CONTENTS="dpkg --contents"

UNPACK_TAR="tar xf -"
UNPACK_CPIO="cpio -idmu"
BUNZIP2="bunzip2 -c"
GUNZIP="gunzip -c"
UNPACK_RAR="unrar"
UNPACK_RPM="rpm -i "
UNPACK_JAR="jar -xf"
UNPACK_ZIP="unzip "
UNPACK_7Z="7za x"
UNPACK_DEB="apt install"

MPLAYER='mplayer -quiet -slave -input file=/home/bhepple/.mplayer/fifo'

# In the following, use single quotes if options are needed eg. 'foo -bar' 
#
# C_ applications will be piped with `| $PAGER' and a console will be created 
# if necessary.
#
# Environment variables can be used with backslash eg \$XPDFGEOM -
# evaluated at runtime (not now)

C_PAGERS="C_PAGER $PAGER less more pg"
X_PAGERS="X_PAGER xmore xless xp 'zenity --text-info --width 800 --height 800 --filename /dev/stdin' 'gxmessage -file -' 'Xdialog --fixed-font --textbox - 0 0' 'xmessage -file -'"
C_EDITORS="C_EDITOR $EDITOR vi vim"
X_EDITORS="X_EDITOR emacs gvim gedit kate"

# VIEWERS & EDITORS
X_AUDIO_PLAYERS="X_AUDIO_PLAYER 'smplayer &>/dev/null' 'xine -H' xmms 'gmplayer -quiet' amarok kaffeine audacious alsaplayer rhythmbox listen exhaile kaboodle juk"
C_AUDIO_PLAYERS="CONSOLE_AUDIO_PLAYER '$MPLAYER' mp3blaster play"
X_AUDIO_EDITORS="X_AUDIO_EDITOR audacity"
C_AUDIO_EDITORS="CONSOLE_AUDIO_EDITOR"

X_GRAPH_VIEWERS="X_GRAPHICS_VIEWER gpicview 'xzgv --zoom' 'feh -d --geometry 900x675' xli xloadimage eog gthumb kuickshow gwenview showfoto"
C_GRAPH_VIEWERS="CONSOLE_GRAPHICS_VIEWER zgv"
X_GRAPH_EDITORS="X_GRAPHICS_EDITOR gimp"
C_GRAPH_EDITORS="CONSOLE_GRAPHICS_EDITOR"

X_PDF_VIEWERS="X_PDF_VIEWER okular kpdf 'xpdf -geometry \$XPDFGEOM' acroread kghostview evince epdfview gv"
C_PDF_VIEWERS="CONSOLE_PDF_VIEWER"
X_PDF_EDITORS="X_PDF_EDITOR"
C_PDF_EDITORS="CONSOLE_PDF_EDITOR $C_EDITOR"

X_CHM_VIEWERS="X_CHM_VIEWER xchm gnochm chmsee kchmviewer ebook-viewer" # gnochm is a bit buggy; chmsee has no search; ebook-viewer is slow
X_EPUB_VIEWERS="X_EPUB_VIEWER lucidor ebook-viewer"
X_MOBI_VIEWERS="X_MOBI_VIEWER ebook-viewer"

X_PS_VIEWERS="X_PS_VIEWER okular kghostview evince gv ggv gimp"
C_PS_VIEWERS="CONSOLE_PS_VIEWER $C_PAGER"
X_PS_EDITORS="X_PS_EDITOR $X_EDITOR"
C_PS_EDITORS="CONSOLE_PS_EDITOR $C_EDITOR"

X_HTML_VIEWERS="X_HTML_VIEWER $BROWSER browser firefox mozilla netscape konqueror dillo opera"
C_HTML_VIEWERS="CONSOLE_HTML_VIEWER links lynx $C_PAGER"
X_HTML_EDITORS="X_HTML_EDITOR $X_EDITOR"
C_HTML_EDITORS="CONSOLE_HTML_EDITOR $C_EDITOR"

X_DOC_VIEWERS="X_DOC_VIEWER 'ooffice -view' 'oowriter2 -view' 'soffice -view' abiword"
C_DOC_VIEWERS="CONSOLE_DOC_VIEWER antiword"
X_DOC_EDITORS="X_DOC_EDITOR ooffice oowriter2 soffice abiword"
C_DOC_EDITORS="CONSOLE_DOC_EDITOR"

X_OODOC_VIEWERS="X_OODOC_VIEWER 'ooffice -view' 'oowriter2 -view'"
C_OODOC_VIEWERS="CONSOLE_OODOC_VIEWER"
X_OODOC_EDITORS="X_OODOC_EDITOR ooffice oowriter2"
C_OODOC_EDITORS="CONSOLE_OODOC_EDITOR"

X_DIR_VIEWERS="X_DIRECTORY_VIEWER"
C_DIR_VIEWERS="C_DIRECTORY_VIEWER 'ls -l'"
X_DIR_EDITORS="X_DIRECTORY_EDITOR konqueror xfe xmc gentoo nautilus"
C_DIR_EDITORS="C_DIRECTORY_EDITOR mc"

X_DVD_VIEWERS="X_DVD_VIEWER '$MPLAYER dvd:// -dvd-device ' 'xine dvd://'"
C_DVD_VIEWERS="C_DVD_VIEWER '$MPLAYER -quiet dvd:// -dvd-device '"

X_VIDEO_VIEWERS="X_VIDEO_VIEWER 'smplayer &>/dev/null' xine 'mplayer -quiet' totem"
C_VIDEO_VIEWERS="C_VIDEO_VIEWER '$MPLAYER'"
X_VIDEO_EDITORS="X_VIDEO_EDITOR"
C_VIDEO_EDITORS="C_VIDEO_EDITOR"

X_SSHEET_VIEWERS="X_SPREADSHEET_VIEWER gnumeric 'ooffice -view' 'ooffice2 -view' 'soffice -view'"
C_SSHEET_VIEWERS="C_SPREADSHEET_VIEWER"
X_SSHEET_EDITORS="X_SPREADSHEET_EDITOR gnumeric ooffice ooffice2 soffice"
C_SSHEET_EDITORS="C_SPREADSHEET_EDITOR"

X_OOSSHEET_VIEWERS="X_OOSPREADSHEET_VIEWER 'ooffice -view' 'ooffice2 -view'"
C_OOSSHEET_VIEWERS="C_OOSPREADSHEET_VIEWER"
X_OOSSHEET_EDITORS="X_OOSPREADSHEET_EDITOR ooffice ooffice2"
C_OOSSHEET_EDITORS="C_OOSPREADSHEET_EDITOR"

X_PRES_VIEWERS="X_PRESENTATION_VIEWER 'ooffice -view'"
C_PRES_VIEWERS="C_PRESENTATION_VIEWER"
X_PRES_EDITORS="X_PRESENTATION_EDITOR ooffice"
C_PRES_EDITORS="C_PRESENTATION_EDITOR"

X_OOPRES_VIEWERS="X_OOPRESENTATION_VIEWER 'ooffice -view'"
C_OOPRES_VIEWERS="C_OOPRESENTATION_VIEWER"
X_OOPRES_EDITORS="X_OOPRESENTATION_EDITOR ooffice"
C_OOPRES_EDITORS="C_OOPRESENTATION_EDITOR"

X_GJOTS_VIEWERS="X_GJOTS_VIEWER 'gjots2 -r' 'gjots -r'"
C_GJOTS_VIEWERS="C_GJOTS_VIEWER $C_PAGER"
X_GJOTS_EDITORS="X_GJOTS_EDITOR gjots2 gjots"
C_GJOTS_EDITORS="C_GJOTS_EDITOR $C_EDITOR"

X_OBJ_VIEWERS="X_OBJ_VIEWER"
C_OBJ_VIEWERS="C_OBJ_VIEWER 'objdump -tTir' nm"
X_OBJ_EDITORS="X_OBJ_EDITOR"
C_OBJ_EDITORS="C_OBJ_EDITOR"

X_MANPAGE_VIEWERS="X_MANPAGE_VIEWER manual"

X_TERMS="X_TERM 'myterm -- -e sh -c' 'konsole -e sh -c' 'rxvt -e sh -c' 'xterm -e sh -c' 'wxterm -e sh -c' 'gnome-terminal -e sh -c' 'xfce4-terminal -e sh -c' 'x-terminal-emulator -e sh -c'"

EDIT=""
ALL=""
[ "$PROG" = "x" ] && EDIT="yes"
[ "$PROG" = "V" ] && PREFER_X="$DISPLAY" && VERBOSE="yes"

VERBOSE=""

NEW_ARGS=( )
check_and_process_opts "$@" && set -- "${NEW_ARGS[@]}"

##############################################################
# Setup the PAGER - it is pretty fundamental to most of this #
##############################################################
# favour less over more unless the user says otherwise:
[ -z "$PAGER" ] && export PAGER="less"
C_PAGER=$(check_exec $C_PAGERS) || exit 1
# I don't much like the X pagers available, but if you do, uncomment this:
X_PAGER=$(check_exec $X_PAGERS)

# setup the editor
C_EDITOR=$(check_exec $C_EDITORS)
X_EDITOR=$(check_exec $X_EDITORS)

[ "$ALL" ] && {
    [ "$VERBOSE" ] && VERBOSE=" -v"
    [ "$PREFER_X" ] && PREFER_X=" -X"
    DIR="${1:-.}"
    IFS=$'\n'
    for F in $(find "$DIR" \( -iname \*.avi -o -iname \(.ogv -o -iname \*.mov -o -iname \*.mpeg -o -name \*.mpg -o -iname \*.wma -o -iname \*.ram  -o -iname \*.mp4 -o -iname \*.wmv -o -iname \*.mp3 -o -iname \*.oga -o -iname \*.ogg -o -iname \*.flac -o -iname \*.mp2 -o -iname \*.wav -o -iname \*.mkv -o -iname \*.m4a \) -print |sort); do
        echo "Viewing: $F"
        eval $PROG $VERBOSE $PREFER_X \""$F"\"
    done
    exit $?
}

# degenerate case: we are being piped to - just run less in the
# current console
[ $# -eq 0 ] && exec $C_PAGER

verbose_out "$PROG $@" 

# Special case: URL:
[[ "$1" =~ ^http:// ]] && exec $BROWSER $1

# salt away our list of files in an array:
FILE=( "$@" )

# See if any filename has a backslash and convert to UNIX
for (( N=0; N<"${#FILE[@]}"; N++ )); do
    case "${FILE[$N]}" in
        *\\* | ?:*)
            FILE[$N]=$(ms2unix "${FILE[$N]}")
            ;;
    esac
done

# Check that all files are readable:
UNREADABLE=""
for A in "${FILE[@]}"; do
    if [ ! -r "$A" ]; then 
        out "$A: No such file or directory"
        UNREADABLE="yes"
    fi
done
[ "$UNREADABLE" ] && exit 1

#FULL=$(fqn "${FILE[0]}") || exit 1
#echo "Actual filename:" 
#echo "$FULL" 

RUN_IN_CONSOLE=""
unset LESSOPEN

if [ "$DISPLAY" ]; then # we probably have X, even if we have PREFER_X=""
    verbose_out "TERM=\"$TERM\"" 
    if [ -z "$TERM" -o "$TERM" = "dumb" ]; then 
        # we're probably running from an X app eg from sylpheed
        if ! XTERM=$(check_exec $X_TERMS); then
            echo "$PROG: Can't find an X terminal" >&2
            exit 1
        fi
        RUN_IN_CONSOLE="$XTERM "
        verbose_out "XTERM=\"$XTERM\"" 
    fi
fi

shopt -s nocaseglob

if [ "$EDIT" ]; then
    :
else
    if [ "$PREFER_X" ]; then
        [ -z "$COM" ] && check_suffix_for_x_viewer
        [ -z "$COM" ] && check_mime_for_x_viewer
        [ -z "$COM" ] && check_suffix_for_console_viewer
        [ -z "$COM" ] && check_mime_for_console_viewer
    else
        [ -z "$COM" ] && check_suffix_for_console_viewer
        [ -z "$COM" ] && check_mime_for_console_viewer
        if [ "$DISPLAY" ]; then
            [ -z "$COM" ] && check_suffix_for_x_viewer
            [ -z "$COM" ] && check_mime_for_x_viewer
        fi
    fi
fi

# if there's no viewer, look for an editor:

if [ "$PREFER_X" ]; then
    [ -z "$COM" ] && check_suffix_for_x_editor
    [ -z "$COM" ] && check_mime_for_x_editor
    [ -z "$COM" ] && check_suffix_for_console_editor
    [ -z "$COM" ] && check_mime_for_console_editor
else
    [ -z "$COM" ] && check_suffix_for_console_editor
    [ -z "$COM" ] && check_mime_for_console_editor
    if [ "$DISPLAY" ]; then
        [ -z "$COM" ] && check_suffix_for_x_editor
        [ -z "$COM" ] && check_mime_for_x_editor
    fi
fi

### this buggers up simple text file viewing eg "v *.c":
### # if nothing matches, use 'cat $(filelist) | less':
### [ -z "$COM" ] && COM=cat

# if we're viewing (not editing) and nothing matches, use C_PAGER:
if [ -z "$COM$EDIT" ]; then
    verbose_out "... can't grok by file type (console)" 
    COM=$C_PAGER
fi

if [ "$COM" = "$C_PAGER" ]; then
    COM="$COM $(filelist)"
fi

if [ -z "$COM" ]; then
    out "don't know how!"
    STATUS=1
else
    verbose_out "Running: '$COM'" 
    eval exec $COM
    STATUS=$?
    verbose_out "result=$STATUS"
fi

exit $STATUS
