#!/bin/bash

#
# fedora-extract
# Extract various archives, "tar xvf" style
#
# Author:  Ville Skyttä <ville.skytta at iki.fi>
# License: GPL
# $Id: fedora-extract,v 1.3 2006/02/26 15:53:55 scop Exp $

# TODO: more archive types

set -e
unset CDPATH
ftype=
decomp=
quiet=
force=
dir=

usage()
{
    echo "Usage: `basename $0` [-qf] [-C dir] ARCHIVE..."
    echo "  -q        Suppress output."
    echo "  -f        Force overwriting existing files."
    echo "  -C dir    Change to directory 'dir' before extracting."
}

fmime()
{
    case "$1" in
        application/x-zip|application/zip)                  ftype=zip  ;;
        application/x-tar*)                                 ftype=tar  ;;
        application/x-rpm)                                  ftype=rpm  ;;
        application/x-archive|application/x-debian-package) ftype=ar   ;;
        application/x-arj)                                  ftype=arj  ;;
        application/x-zoo)                                  ftype=zoo  ;;
        application/x-lha*)                                 ftype=lha  ;;
        application/x-rar)                                  ftype=rar  ;;
    esac
}

ftype()
{
    t=`file -ibL "$1" 2>/dev/null`
    fmime "$t"
    [ -n "$ftype" ] && return

    case "$t" in
        application/x-compress|application/x-gzip)  decomp="gzip -dc"  ;;
        application/x-bzip2)                        decomp="bzip2 -dc" ;;
    esac

    if [ -n "$decomp" ] ; then
        t=`file -zibL "$1" 2>/dev/null`
        fmime "$t"
        [ -n "$ftype" ] && return
    fi

    t=`file -${decomp:+z}bL "$1" 2>/dev/null`
    case "$t" in
        *Z[iI][pP]\ *archive*)                              ftype=zip  ;;
        *tar\ archive*)                                     ftype=tar  ;;
        *ar\ archive*|*Debian\ *package*)                   ftype=ar   ;;
        *ARJ\ *archive*)                                    ftype=arj  ;;
        *Zoo\ *archive*)                                    ftype=zoo  ;;
        *cpio\ archive*)                                    ftype=cpio ;;
        *C[aA][bB]*archive*)                                ftype=cab  ;;
        RPM\ *)                                             ftype=rpm  ;;
        *ACE\ *archive*)                                    ftype=ace  ;;
        *LHa\ *archive*)                                    ftype=lha  ;;
        *RAR\ *archive*)                                    ftype=rar  ;;
    esac
}

unarch()
{
    case "$1" in
        /*) f="$1" ;;
        *)  f="$PWD/$1" ;;
    esac
    ftype "$1"
    cd "$2"
    case "$ftype" in
        tar)
            [ -n "$force" ] && o= || o=k
            ${decomp:-cat} "$f" | tar xv$o
            ;;
        zip)
            [ -n "$force" ] && o=-o || o=-n
            unzip $o "$f"
            ;;
        rar)
            [ -n "$force" ] && o=+ || o=-
            unrar x -o$o -y "$f"
            ;;
        cab)
            # force not supported, it's always on (as of cabextract 1.[01])
            cabextract -f "$f"
            ;;
        rpm)
            name=`rpm -qp --qf "%{NAME}-%{VERSION}-%{RELEASE}" "$f"`
            mkdir -p "$name"
            cd "$name"
            rpm2cpio "$f" \
            | cpio --quiet --no-absolute-filenames -id${force:+u}mv 2>&1 \
            | sed "s|^\(\./\)\?|$name/|"
            cd ..
            ;;
        cpio)
            ${decomp:-cat} "$f" | \
                cpio --quiet --no-absolute-filenames -id${force:+u}mv
            ;;
        ar)
            # force not supported, it's always on
            ar xvo "$f"
            ;;
        ace)
            [ -n "$force" ] && o=+ || o=-
            unace x -o$o -y "$f"
            ;;
        arj)
            # force not supported, it's always off (as of unarj 2.6[35])
            # it will also return an error if some files already exist :(
            unarj x "$f"
            ;;
        zoo)
            zoo x${force:+OOS} "$f"
            ;;
        lha)
            lha x${force:+f} "$f" < /dev/null
            ;;
        *)
            echo "Error: unrecognized archive: '$f'" >&2
            exit 1
            ;;
    esac
    cd - >/dev/null 2>&1
}

dir="$PWD"

while getopts "qfC:" key ; do
    case "$key" in
        q) quiet=1 ;;
        f) force=1 ;;
        C) dir="$OPTARG" ;;
        *) usage ; exit 1 ;;
    esac
done
shift $(( $OPTIND -1 ))

if [ ! -d "$dir" ] ; then
    [ -e "$dir" ] && \
        echo "Error: not a directory: '$dir'" >&2 ||
        echo "Error: directory does not exist: '$dir'" >&2
    exit 1
fi

for file in "$@" ; do
    if [ ! -f "$file" ] ; then
        [ -e "$file" ] && \
            echo "Error: not a regular file: '$file'" >&2 ||
            echo "Error: file does not exist: '$file'" >&2
        exit 1
    fi
    # Not that fancy at the moment, but -q is for backwards compatibility
    # and in case we need to do more fine-grained stuff for some unarchivers
    # later.
    if [ -n "$quiet" ] ; then
        unarch "$file" "$dir" >/dev/null
    else
        unarch "$file" "$dir"
    fi
done
