#!/bin/bash
# -*- coding: utf-8 -*-

# rpmdev-extract -- Extract various archives in "tar xvf" style
#
# Copyright (c) 2004-2007 Fedora Project <http://fedoraproject.org/>.
# Author: Ville Skyttä <scop at fedoraproject.org>
#
# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

# TODO: more archive types

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

shopt -s extglob

version()
{
    cat <<EOF
rpmdev-extract version 1.1

Copyright (c) 2004-2007 Fedora Project <http://fedoraproject.org/>.
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.

Written by Ville Skyttä.
EOF
}

help()
{
    cat <<EOF
rpmdev-extract extracts various archives in "tar xvf" style.

Depending on availability of external (un)archiver programs, the following
archive types are supported: ace, ar/deb, arj, cab/exe, cpio, lha, rar, rpm,
tar, zip/jar, zoo.

EOF
    usage
    echo ""
    echo "Report bugs to <http://bugzilla.redhat.com/>."
}

usage()
{
    cat <<EOF
Usage: rpmdev-extract [OPTION]... ARCHIVE...

Options:
  -q        Suppress output.
  -f        Force overwriting existing files.
  -C DIR    Change to directory DIR before extracting.
  -h        Print help message and exit.
  -v        Print version information and exit.
EOF
}

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"`
            if [ -z "$name" ] ; then # workaround for #250990
                bname="`basename "$f"`"
                name=${bname/?(\.*([^\.]))\.rpm/}
            fi
            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:hv" key ; do
    case "$key" in
        q) quiet=1 ;;
        f) force=1 ;;
        C) dir="$OPTARG" ;;
        h) help ; exit 0 ;;
        v) version ; exit 0 ;;
        *) 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
