#!/bin/bash

# Select device or partition
# Version 9.07.06 by Andreas Mandalka


#file where selection is stored temporarily
TMPFILE=/tmp/selection.$$
FSTAB=/etc/fstab
CRYPTTAB=/etc/crypttab

function help() {
  cat<<EOF
USAGE
  ${0} [OPTIONS]

-f|--filter	   Set filter (default: partitions).
		   Other values: devices | luks | usb-partitions | usb-devices
-o|--outputfile	   Store selected device-node (/dev/sdXX) in this file.
-t|--title	   Title for dialog.
-m|--message	   Message for dialog.
-d|--dialog	   Tool for dialog (default: dialog)
		   Other value: Xdialog
-nt|--none-text	   Entry for "no selection" in dialog.
-nd|--none-desc	   Description for "no selection" in dialog.
--exclude-crypttab No devices that are used in /etc/crypttab
--exclude-fstab	   No devices that are used in /etc/fstab

EOF
}

function parse()
{
    if [ ! "$#" -gt "0" ]; then
	help
        exit 0
    fi

    while [ "$#" -gt "0" ]; do
        case ${1} in
	    -f|--filter)
                FILTER="${2}"
                shift 2
                ;;
	    -o|--outputfile)
                OUTPUTFILE="${2}"
                shift 2
                ;;
	    -t|--title)
		SEL_TITLE="$(doifs "${2}")"
		shift 2
		;;
	    -m|--message)
		SEL_MESSAGE="$(doifs "${2}")"
		shift 2
		;;
	    -d|--dialog)
		SEL_DIALOG="$(doifs "${2}")"
		shift 2
		;;
	    -nt|--none-text)
		SEL_NONE="$(doifs "${2}")"
		shift 2
		;;
	    -nd|--none-desc)
		SEL_NONE_DESC="$(doifs "${2}")"
		shift 2
		;;
	    --exclude-crypttab)
		NOCRY="true"
		shift
		;;	    
	    --exclude-fstab)
		NOFST="true"
		shift
		;;	    
	    -h|--help|*) #gives you a short help
	       help
 	       exit 0
	       ;;
        esac
    done

    [ -z "${OUTPUTFILE}" ] && echo "You must specify an output file." && help &&  exit 240
    
    #if not specified, use following:
    [ -z "${SEL_NONE_DESC}" ] && SEL_NONE_DESC="NONE"
    [ -z "${SEL_TITLE}" ] && SEL_TITLE="Select device"
    [ -z "${SEL_DIALOG}" ] && SEL_DIALOG="dialog"
}


function get_crypttab()
#Liste mit allen dev auslesen
{
	_LIST=$(grep -v '^#' ${CRYPTTAB} | awk '{ print $2 }' | sort | uniq)
	CRYPTTABLIST=""
	for item in ${_LIST}
	do
		getdev_from ${item}
		CRYPTTABLIST="${CRYPTTABLIST} $ISDEVICE"

	done

}


function get_fstab()
#Liste aller Devices aus /etc/fstab
{
	_LIST=$(grep -v "^#" ${FSTAB} |  awk '{ print $1 }' | sort | uniq)
	FSTABLIST=""
	for item in ${_LIST}
	do
		getdev_from ${item}

		FSTABLIST="${FSTABLIST} $ISDEVICE"

	done
}


function getdev_from()
#nimmt /dev/disk/by-xxx, /dev/sdx1, /block/hdx/hdxxxx und UUID Geraetereferenzen an
# und gibt /dev/abc zurueck.
#wenn eine UUID z.B. ein auf ein Geraet /dev/mapper/xyz referenziert, wird es nicht ausgewertet.
{
	ISDEVICE=""
	WHICHDEV=$1

	#wenn anfaengt mit /block
	if [ $(echo "${WHICHDEV}"| grep "^/block/") ]; then
		ISDEVICE=$(udevadm info --query=name -p "${WHICHDEV}" | sed s/[0-9]//g)
		return 0	
	fi


	
	if [ $(echo "${WHICHDEV}"| grep "^/dev/") ]; then
		if [ $(echo "${WHICHDEV}"| grep "^/dev/disk") ]; then
			#wenn /dev/disk/by-xxx/yyyyy vorliegt
			ISDEVICE=$(ls -l "${WHICHDEV}" | egrep -o '/[a-z0-9]*$'| cut -d"/" -f2  | sed s/[0-9]//g)
			return 0
		fi		

		#wenn anfaengt mit /dev/mapper
		if [ $(echo "${WHICHDEV}"| grep "^/dev/mapper") ]; then
			return 0	
		fi


		#wenn direkt /dev/hda1 vorliegt
		ISDEVICE=$(echo "${WHICHDEV}" | grep -o '[a-z0-9]*$' | sed s/[0-9]//g)
		return 0
	fi

	if [ $(echo "${WHICHDEV}"| grep -i "^UUID=") ]; then
		#wenn UUID vorliegt
		#UUID entfernen
		WHICHDEV=$(echo "${WHICHDEV}" | sed s/UUID=//)
		ISDEVICE=$(ls -l "/dev/disk/by-uuid/${WHICHDEV}" | egrep -o '/[a-z0-9]*$'| cut -d"/" -f2  | sed s/[0-9]//g)
		return 0	
	fi

	#wenn bisher nix angeschlagen hat, haben wir nix was wir brauchen :P
	return 1
}



function doifs() {
	#You cant use spaces in dialog within MESSAGEs, titles, etc. So we need to replace spaces with IFS.
	IFS=$'\t';
	echo -n $(echo "${1}" | tr '[ ]' '[\t]')
}

function get_list() {
	case ${FILTER} in
            "devices")
                GREP_FILTER="/block/[hs]d[a-z]$"
                ;;
            "usb-devices")
                GREP_FILTER="/block/[s]d[a-z]$"
                ;;
            "luks")
                GREP_FILTER="/block/[hs]d[a-z]/.*"
                ;;
	    *)
		GREP_FILTER="/block/[hs]d[a-z]/.*"
		;;
        esac


	#In crypttab und/oder fstab verwendete Geraete auslesen
	[ "${NOCRY}" == "true" ] && get_crypttab		
	[ "${NOFST}" == "true" ] && get_fstab


	for item in $(udevadm info --export-db| egrep -o "${GREP_FILTER}"| uniq);
	do
		_device="$(udevadm info --query=name -p ${item})"
		device="/dev/$_device"

		#wenn eines der devices in einer der Listen vorkommt, einfach weitermachen und somit das Geraet ignorieren
		[ "${NOCRY}" == "true" ] && [ "$(echo $CRYPTTABLIST | grep $_device)" ] && continue
		[ "${NOFST}" == "true" ] && [ "$(echo $FSTABLIST | grep $_device)" ] && continue

		if [ "${FILTER}" == "usb-partitions" -o "${FILTER}" == "usb-devices" ];
		then
                  bus=$(udevadm info --query=env -p ${item} 2>/dev/null | grep "ID_BUS=" | cut -f2 -d'=' | sed s/_/\ /g)
        
                  if ! [ "$bus" == "usb" ]; then
                    continue;
                  fi
	        fi;

		if [ "$FILTER" == "luks" ];
		then
			cryptsetup isLuks $device 2>/dev/null
			if [ $? == 0 ];
			then
				LIST="$LIST $device"
			fi
		else
			LIST="$LIST $device"
		fi
	done
}

function makemenu() {
	for item in ${LIST};
	do
		UDEV_NODE=$(udevadm info -q path -n ${item})

		model=$(udevadm info --query=env -p ${UDEV_NODE} 2>/dev/null | grep "ID_MODEL=" | cut -f2 -d'='|sed s/_/\ /g| tr -d [:punct:])
		vendor=$(udevadm info --query=env -p ${UDEV_NODE} 2>/dev/null | grep "ID_VENDOR=" | cut -f2 -d'='|sed s/_/\ /g| tr -d [:punct:])
		bus=$(udevadm info --query=env -p ${UDEV_NODE} 2>/dev/null | grep "ID_BUS=" | cut -f2 -d'=' | sed s/_/\ /g)
		size=$(LC_ALL=C fdisk -l "$item" 2>/dev/null | grep Disk | grep -i bytes | cut -f1 -d','| cut -f3,4 -d' ')
		
		#skip extended Partitions
		if [ -z "$size" ]; then
			continue 
		fi
	
		case "$LANG" in
		de*|at*|ch*)
			[ "$bus" == "usb" ] && bus="USB (Extern)"
			[ "$bus" == "ata" ] && bus="ATA (Intern)"
			[ "$bus" == "scsi" ] && bus="SCSI/SATA (Intern)"
			;;
		*)
			[ "$bus" == "usb" ] && bus="USB (external)"
			[ "$bus" == "ata" ] && bus="ATA (internal)"
			[ "$bus" == "scsi" ] && bus="SCSI/SATA (internal)"
			;;
		esac
		
		info="${size} | ${bus} | ${model}"
		[ -n "$vendor" ] && info="$info ($vendor)";
		IFS=$'\t';
		menuitems="${menuitems}${IFS}${item}${IFS}${info}";
		unset IFS;
	done

	unset IFS;
	IFS=$'\t'	

	if [ -n "$SEL_NONE" ];
	then
	  menuitems="${menuitems}${IFS}${SEL_NONE}${IFS}${SEL_NONE_DESC}";
        fi
 
	if ! $SEL_DIALOG --title "${SEL_TITLE}" --menu "${SEL_MESSAGE}" 0 0 8 ${menuitems} 2>$TMPFILE
	then
		#abort by user
		exit 255
	fi
	
	cat ${TMPFILE} > ${OUTPUTFILE}
	clean
	
}	

function clean() {
	rm "$TMPFILE"
}

trap 'test -e $TMPFILE && rm $TMPFILE' 2 15

parse "$@"

get_list
makemenu
