#!/bin/bash
#
################################################################
# rpmget -- a bash script to extract a specified file
#           from a given rpm package
#
# Usage:  rpmget  <rpm-package-file>  <string-to-match>  [-list]
#
#   Using rpmget, one can easily extract a specified file from a rpm
# package (binary or source), as shown in the following examples:
# 
#   - To read the README/doc file contained in a rpm package, some
#     examples:
#       rpmget foobar.i386.rpm  README  | more
#	rpmget /usr/src/redhat/SRPMS/foo.src.rpm  spec  | less	
#   - To extract and store a particular file from a rpm package, some
#     examples:
#	rpmget libc-5.4.23-1.i386.rpm libc.so.5.4.23 > libc.so.5.4.23
#       rpmget foobar.i386.rpm jpegdir/bar.jpg > new.jpg
#        [Note that here, "jpegdir/bar.jpg" string has uniquely identified
# 	  a single file (from its pathname) contained in the rpm package.]
#  
#  We can list the files in a rpm package by "rpm -qpl <rpm-package>".
# But to extract a single file printed to the standard output, rpmget
# can be used. Works for both binary or source rpms.
#     If "-list" option is used, then a list of matching files is shown
# (but no file belonging to the rpm package is emitted to the stdout).
# Examples:
# rpmget local/foobar.i386.rpm -list  # list all files in that rpm
# rpmget foo.i386.rpm .doc -list  # list all files with .doc in their names
# 
#     If "-list" is not used, then a single unique file whose pathname
# (as shown by the rpm -qpl command) has matched (at least in part) the
# given string is catted to the stdout. If no single unique match is
# found, then rpmget just prints a error message and exits.
#     The string can be specified as a contiguous set of characters
# (*not* a regular expression) that should be part of the full pathname
# of the file to be extracted. The string characters are case
# sensitive. Caveat: don't use a string starting with a leading '/'
# character, else you may get incorrect result.
#
#  rpmget (c) Subhas Roy  subhas@pobox.com   1997   GPL 
######################################################################

pgm="rpmget"
version="1.1"
last_mod="97/02/26"
authorinfo="Subhas Roy subhas@pobox.com"
copyright="Distrib.: GPL"

intro="$pgm ver $version $last_mod $authorinfo $copyright\n
\tA bash script to extract a specified file from a given rpm package\n"

usage="Usage: $pgm <rpmfile> [<string-to-match>]\n\
\t[-l | -list] [-h | -help] [-<num>]\n"
helpmsg="To do read detailed help, do: more $0\n"

rpmget_tmpdir="/tmp/rpmget_tmpdir.01"

cleanup_tmp() {
    rm -rf $rpmget_tmpdir
}

handle_error() {
 {
    printf "** Error:\n\t$*\n\nTo see help text, do: $pgm -h\n"
    cleanup_tmp
    exit 1
 } >&2
}

# process command-line arguments

if [ $# -gt 4 ]; then
    handle_error "too many arguments"
fi

while [ $# -gt 0 ]
do
    case $1 in
    -V|-ver|--version)
	printf "\n\t$pgm\n\tversion\t$version\n\t\
last modification\t$last_mod\n"
	shift
	exit 0
	;;
    -h|--help|-help)
	printf "$intro\n$usage$helpmsg"
	shift
	exit 0
	;;
    -l|--list|-list)
	just_listing="true"
	shift
	;;
    -[0-9]*)
	choose_by_num="true"
	filenum="$(expr match $1 '^-\([0-9]+\)$')"
	if [ -z "$filenum" ]; then
	    handle_error "incorrect number"
	fi
	shift
	;;
    *)
	if [ $(expr match "$1" '^.*\.rpm$') -eq 0 ]; then
	    arg_str=$1
	else
	    arg_rpmfile=$1
	fi
	shift
	;;
    esac
done

if [ -z "$arg_rpmfile" ]; then
    handle_error "specify the rpm package file"
fi

if [ -z "$arg_str" ] && [ "$choose_by_num" != "true" ] ; then
    # if just the rpm is specfied, then just list all files
    arg_str=""
    just_listing="true"
fi

# get the complete path name of the rpm in rpmfilepath
if [ $(expr match "$arg_rpmfile"  '^/.*') -eq 0 ]; then
    rpmfilepath=$(pwd)/$arg_rpmfile
else
    rpmfilepath=$arg_rpmfile
fi

if [ ! -f "$rpmfilepath" ]; then
   handle_error "cannot open file $rpmfilepath"
fi

rm -rf $rpmget_tmpdir
mkdir $rpmget_tmpdir
cd $rpmget_tmpdir

# unpack the matching files in tmpdir using rpm2cpio and cpio
rpm2cpio $rpmfilepath  | \
	cpio -i -d --no-absolute-filenames "*${arg_str}*" > \
	/dev/null 2>&1

matched_files="$(find . -type f -print | tr -s '[:space:]' ' ')"

if [ -z "$matched_files" ]; then
    handle_error "No files matching ${arg_str} found in\n\t$rpmfilepath"
fi

set $matched_files

if [ "$choose_by_num" = "true" ]; then
    if [ $filenum -gt 0 ] &&  [ $filenum -le $# ]; then
	count=0
	for arg in $*
	do
	    count=$((count + 1))
	    if [ $count -eq $filenum ]; then
		file_to_cat=$arg
		break;
	    fi
	done
    else
	handle_error "number not in range"
    fi
fi

if  [ "$choose_by_num" != "true" ] && [ $# -gt 1 ] || \
	[ "$just_listing" = "true" ]; then
    printf "The file(s) matching ${arg_str} are:\n"
    count=1
    for file in $*
    do
	printf "$count. $(expr $(expr match $file '^\.\(.*\)$') '|' $file)\n"
	count=$((count + 1))
    done
fi

if [ "$choose_by_num" != "true" ] && [ "$just_listing" != "true" ]; then
    if [ $# -gt 1 ]; then
 	handle_error \
	    "multiple files matching $arg_str found in\n\t$rpmfilepath.\n
	    \tChoose one using -<num> option."
    else
	file_to_cat=$1
    fi
fi

if [ "$just_listing" != "true" ]; then
    cat $file_to_cat
fi

cleanup_tmp

exit 0

###################################################################


