#!/bin/bash

#: Title		: findgrub
#: Date Created : Wed Sep 29 20:02:40 CDT 2010
#: Last Edit	: Sat Oct 20 15:23:06 PDT 2012
#: Author		: please_try_again & edited by j McDaniel
#: Version		: 4.4.1
#: Description	: Locates the Grub Boot Loader & Windows BootLoader, Creates Grub Menu.Lst Entries
#: Options		: -h | --help, -w | -- writemenu, -k | --kernel, -a | --activate, -s | --coresize, -d | --debug, -m | --map
#: 				: -v | --verbose, -c --core, -n | --nocolor
# Created for the openSUSE forums September 30th in the year 2010 with loving care and presented to all of our great openSUSE USERS!
#
# Copy and paste this text into a text file and save it in your home area, bin folder (~/bin) as the file findgrub
# Once the file is saved, you must make the file executable using the terminal command: chmod +u ~/bin/findgrub
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# Thanks to Ulrich Meierfrankenfeld and Gert Hulselmans for the Boot Info Script: http://bootinfoscript.sourceforge.net
# which helped me to understand how to read stage2 location from lzma compressed core in Grub 1.99. 

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# current version
version="4.4.1"

# default mountpoint
mnt=/mnt

# default device.map
devmap=/boot/grub/device.map
[ -f $devmap ] || devmap=/boot/grub2/device.map
[ -f $devmap ] || devmap=/boot/grub2-efi/device.map

menu=/boot/grub/menu.lst

sfdisk=/sbin/sfdisk
fdisk=/sbin/fdisk
udevadm=/sbin/udevadm
[ -f $udevadm ] || udevadm=udevadm

# Check to see if we are root

if [[ $UID -ne 0 ]]; then
	id -Gn | grep -q wheel || echo "Root User Permissions are required, Please Enter the ..."
	echo
	sudo $0 $1
	exit 0
fi

# Generic MBR
declare Mc031 Mc033
Mc031="SUSE Generic MBR"
Mc033="Windows Generic MBR"

# Grub version
declare GV5272 GVaa75 GV7c3c GV020
GV5272="Grub"
GVaa75="Grub"
GV7c3c="Grub2"
GV020="Grub2"
GV7c3c_ver="1.98"
GV020_ver="1.99"

# Grub signature offset
declare G377 G383 G385 G384 G392
G392="Ubuntu"
G384="Debian"
G377="Fedora"
G383="Mandriva/ArchLinux/Debian"
G384="openSUSE"
G385="openSUSE"

# Grub2 core offset in MBR (don't know how reliable)
CO3784="1.99" # Ubuntu, openSUSE 12.1
CO3356="2.00" # openSUSE 12.2

# Grub2 Core size
CS61520="Fedora"
CS53352="openSUSE"
CS44324="openSUSE"
CS41452="Ubuntu/Mint"
CS42508="Ubuntu"
CS44192="ArchLinux"

# Grub2 magic numbers
# I don't know if these numbers are related to Grub2 version 1.99 or 2.00
# I just noticed that 1.99 uses  52e82801 on Ubtuntu and 2.00 5256be1b on
# openSUSE.
gmagic199='52e82801'
gmagic200='5256be1b'

# Grub2 lzma core sign
G2SIGN=""
Ld1e9dffeffff0000="1.99"
Ld1e9dffeffff8db6="2.00"
Ld1e9dffeffff6690="2.00"

# colors
red='[31;1m'; dred='[31m'
gre='[32;1m'; dgre='[32m'
yel='[33;1m'; dyel='[33m'
blu='[34;1m'; dblu='[34m'
mag='[35;1m'; dmag='[35m'
cya='[36;1m'; dcya='[36m'
whi='[37;1m'; noc='[37;0m'
sgr=$(tput sgr0)

# line width
w=80

printf "Find Grub Version %s - Written for openSUSE Forums\n" $version

function hr {
	[ "$1" == "N" ] && printf "\n" && return
	[ "$1" == "n" ] && printf "\n" && shift
	printf -vC "%${w}s" ""; printf "%s\n%s" "${C// /$1}" $sgr
}

function help {
	cat << EOFHELP

options:
$whi-h --help$noc      : show this help
$whi-k --kernel$noc    : look for linux kernels
$whi-w --writemenu$noc : add boot entries to $menu
$whi-a --activate$noc  : set boot flag on grub partition
$whi-d --debug$noc     : print Win bootsectors to standard output
$whi-s --coresize$noc  : print Grub2 uncompressed core size
$whi-v --verbose$noc   : print offset of stage2 (if using block lists)
$whi-c --core$noc      : show path of core/stage2
$whi-m --map$noc       : check device.map
$whi-n --nocolor$noc   : disable color output

EOFHELP
exit
}

function checkDevmap {
	dmps=($(awk '{ print $2 }' $devmap))
	if [ ${#dmps[*]} -gt 0 ]; then
		for dmp in ${dmps[*]}; do
			[ -b $dmp ] || printf "\n%s%s %s!invalid drive in device.map: %s%s%s" "$sgr" "$DBG" "$red" "$whi" "$dmp" "$sgr"
		done
	fi
}

function devmapWarning {
hr \*
m=$1; shift
case $m in
	1)
	for d in $NOMAP; do
		printf "%sWARNING: %s%s %sis NOT in %s\n" "$red" "$whi" $d "$red" "$devmap"
	done ;;
	2) printf "%sWARNING: /boot/grub/device.map not found.\n" "$red" ;;
esac
printf "\t Displayed BIOS device mapping may be incorrect!%s" "$sgr"
hr n \*
}

function unlzmaWarning {
hr \*
printf "%sWARNING: %s%s\n" $red "$LZMAWARN" $sgr
hr \*
}

function Linux2LegacyGrub {
	d=${1:0:3}; n=${1:3:1}; D=${!d}; d=${D:-hd?}
	let n--
	printf "rootnoverify (%s,%s)\n" $d $n
	if [ "$d" != "hd0" ]; then
		printf "    map (%s) (hd0)\n    map (hd0) (%s)\n" $d $d
	fi
}

function chainloadEntry { 
D=$(basename $1)
if [ "${WRITEMENU}" ]; then
	printf " - append boot entry to chainload Windows bootmanager on %s%s%s to %s%s%s\n" "$red" $1 "$sgr" "$whi" "$menu" "$sgr"
	cp $menu{,.findgrub}
	cat << EOFWriteGrubEntry | sed '/^  *$/d' >> $menu

###Don't change this comment - YaST2 identifier: Original name: WindowsBootLoader###
title Windows on $1
    $(Linux2LegacyGrub $D)
    chainloader +1

EOFWriteGrubEntry
else
	[ "$VERBOSE" == "1" ] && return
	hr n \~
	printf "You can add the following entry to %s%s%s :\n" "$whi" "$menu" "$sgr"
	cat << EOFDisplayGrubEntry | sed '/^  *$/d'
$gre
###Don't change this comment - YaST2 identifier: Original name: WindowsBootLoader###
title Windows on $1
    $(Linux2LegacyGrub $D)
    chainloader +1$sgr
EOFDisplayGrubEntry
	hr \~
fi
}

function printbs {
hr n \~
printf "%s%s %sBootsector\n" "$whi" $1 "$sgr"
hr -
dd if=$1 bs=512 count=1 2>/dev/null | tr -cd "[:alnum:] [:space:]"
hr n -
dd if=/dev/sda1 bs=512 count=1 2>/dev/null | od -x | awk 'BEGIN {m="'"$dmag"'"; y="'"$yel"'"; r="'"$red"'"; s="'"$sgr"'"}; {if ( NF == 9 ) {printf "%s%s|%s %s %s %s %s %s %s %s", m, $1, y, $2, $3, $4, $5, $6, $7, $8; if ( $9 == "aa55" ) printf " %s%s%s\n", r, $9, s; else printf " %s%s\n", $9, s}}'
hr \~
}

function printptbl {
hr \~
printf "%s%s%s Partition table\n" "$whi" $1 "$sgr"
hr -
dd if=$1 bs=1 skip=446 count=64 2>/dev/null | od -x | head -4 | awk 'BEGIN {m="'"$dmag"'"; r="'"$red"'"; g="'"$gre"'"; w="'"$whi"'"; s="'"$sgr"'"}; {HD=substr($2,1,2); BF=substr($2,3,2); FE=substr($4,1,2); ID=substr($4,3,2); if ( BF == "80" ) BF=r BF; printf "%s%s|%s %s%s %s%s %s%s%s %s%s %s %s %s %s%s\n", m, $1, g, HD, BF, g, $3, FE, w, ID, gre, $5, $6, $7, $8, $9, s}'
hr \~
}

function searchkernel {
	pt=$1
	unset kernels ismounted
	mount | grep -q "$pt " && ismounted=1
	eval `blkid $pt | sed 's|.*: ||;s| |;|g'`

	if [ "$ismounted" ]; then
		mp=`mount | grep "$pt " | awk '{ print $3 }'`
		if [ "$mp" == "/boot" -o "$mp" == "/" ]; then
			kernels=`find /boot -type f -name "vmlinuz*"`
 		fi
	else
		mount -t $TYPE $pt $mnt
	
		if [ -d /mnt/boot ]; then
			kernels=`find $mnt/boot -type f -name "vmlinuz*"`
		else
			kernels=`find $mnt -maxdepth 1 -type f -name "vmlinuz*"`
		fi
		umount $mnt 2> /dev/null
	fi

	if [ "$kernels" ]; then
		hr n \~
		for kern in $kernels; do
			printf " --> %skernel %s%-51s %sfound in %s%s%s\n" "$whi" "$blu" "${kern##*/}" "$whi" "$blu" "$pt" "$sgr"
		done
		hr \~
	fi
}

function findbootmgr {
	case $3 in
		6|b|c|e) fs=vfat ;;
		7) fs=ntfs ;;
	esac

	mp=`mount | grep "$1 on" | awk '{ print $3 }'`

	if [ "x$mp" == "x" ]; then
		mp=$mnt
		mount | grep -q "$mp " && mp=/findgrub
		[ -d $mp ] || mkdir $mp
		mount -t $fs $1 $mp
		UNMOUNT=1
	else
		unset UNMOUNT
	fi
	bootmgr="Windows7/Vista"
	ntldr="Windows NT/2K/XP"

	case $2 in
		bootmgr|ntldr)
			find $mp -maxdepth 1 -iname "$2" | grep -q -i "$2" && {
			printf " --> %s%s %sLoader found in %s%s%s\n" $whi "${!2}" "$sgr" "$red" "$pt" "$sgr"
			chainloadEntry $pt
		}
	;;
	esac

	if [ "$UNMOUNT" ]; then
		umount $mp
	fi
	[ -d /findgrub ] && rmdir /findgrub
}

function showCore {
	[ -x /sbin/blkid ] || return
	[ -x /sbin/tune2fs ] || return
	[ -x /sbin/debugfs ] || return
	[ "$1" -a "$2" ] || return
	echo $1 | grep -q '?' && return
	[ $2 -ge 63 ] || return
	Pt=$1; Cof=$(($2*1)); 
	Dr=${Pt:0:3}; par=/dev/$Dr 
	[ -b $par ] || return
	St="S$Dr"
	for s in ${!St} ; do
		o=${s%:*} d=${s#*:}
		if [ $Cof -gt $o ] ; then
			par=/dev/$d ; Pof=$o
		fi
	done
	# returm if partition is not ext2/3/4
	/sbin/blkid -o value -s TYPE $par | grep -q 'ext[2-4]' || return
	[ "$Pof" ] || return
	[ $(($Pof * 1)) -eq 0 ] && return
	lba=$(($Cof - $Pof))
	bsize=$(/sbin/tune2fs -l $par | awk '/Block size/ { print $NF}')
	bsize=${bsize:-4096}
	B=$(($lba * 512/$bsize))
	inode=$(/sbin/debugfs -R "icheck $B" $par 2>/dev/null | awk 'END { print $NF }')
	if [ "$inode" == "found>" ]; then
		Pathname="invalid"
		Inode="<block not found>"
	else	 
		[ "$inode" ] && [ $(($inode * 1)) -gt 0 ] && eval $(debugfs -R "ncheck $inode" $par 2>/dev/null | awk 'END { printf "Inode=%s;Pathname=\"%s\";", $1, $2 }')
		[ "$Inode" ] || Inode="?"
		[ "$Pathname" ] && Pathname="(${!Dr})$Pathname" || Pathname="invalid"
	fi
	case ${Pathname##*/} in
		stage2) CoreCol=$whi;;
		core.img) CoreCol=$yel;;
		*) CoreCol=$red;;
	esac
	printf "\n%98s inode: %s%-8s %sPath: %s%s%s" "=>" "$blu" "$Inode" "$sgr" "$CoreCol" "$Pathname" "$sgr"
}

function stage2 {
	INVALID=""
	dev=${1:0:8}; DEV=${1##*/}
	ST2POS=$(hexdump -v -s 68 -n 4 -e '"%u"' $1)
	drv=$(hexdump -v -s 64 -n 1 -e '"%d"' $1)
	if [ $ST2POS -eq 1 ] ; then	# stage 1.5
		drv=$((drv-127))
		if [ $drv -eq 128 ]; then
			DEV=$(basename $dev)
		else
			drv=$(hexdump -v -s 1050 -n 1 -e '"%d"' $dev)
			drv=hd$(($drv-128))
			drv=${!drv}
			DEV=$(basename $drv)
 		fi
		tmp=$(dd if=$dev skip=1 count=1 2>/dev/null | hexdump -v -n 4 -e '"%x"')
		if [ "$tmp" == "3be5652" -o "$tmp" == "bf5e5652" ]; then # stage2 found
			boot=$(dd if=$dev skip=2 count=1 2>/dev/null | hexdump -v -e '"%_u"' | sed 's|.*0.97nul||;s|stage2.*|stage2|')
			ST2DEV=$DEV$(($(hexdump -s 1049 -n 1 -v -e '"%d"' $dev)+1))
		fi
	else
		if [ "${!DEV}" == "f" ]; then
			if [ $drv -eq 255 ]; then
				DEV=$(basename $dev)
			else
				drv=$(hexdump -v -s 62 -n 94 -e '"%_u"' $1 | sed 's|nul||g;s|etx||g;s|stx||g' | hexdump -v -n 2 -e '"%_u"' | sed 's|8|hd|')
				dev=/dev/$(basename ${!drv})
			fi
		fi
		S=S${dev##*/}
		for p in ${!S}; do
			s=${p%:*}; h=${p#*:}
			[ $ST2POS -gt $s ] && ST2DEV=$h
		done

		dd if=$dev bs=512 count=1 skip=$ST2POS 2>/dev/null | grep -q stage2 || INVALID="INVALID"
	fi
	echo $ST2DEV:${!ST2DEV}@${ST2POS}${INVALID}
}


function Grub2ver {
	tmpdir=${TMPDIR:-/tmp}
	core=$tmpdir/core.img
	if [ -f $core ] ; then
		grub2sign="L$(hexdump -v -n 10000 -e '1/1 "%02x"' $core | awk '{ found_at=match($0, "d1e9dffeffff" ); if (found_at == "0") { print "0" } else { print substr($0,found_at,16)}}')"
		[ "${!grub2sign}" ] && echo ${!grub2sign} || echo $1
		rm $core
	else
		echo $1
	fi
}

function lzmacore {
	# part of this code was borrowed from Boot Info Script: http://bootinfoscript.sourceforge.net
	tmpdir=${TMPDIR:-/tmp}
	if [ -d $tmpdir ]; then
		core=$tmpdir/core.img; ucore=$tmpdir/ucore.img; clog=$tmpdir/ulog
		dsk=$1
		dd if=$2 of=$core skip=$3 count=1024 2>/dev/null
        eval $(hexdump -v -n 10000 -e '1/1 "%02x"' $core | awk '{ lzp=match($0, "d1e9dffeffff" ); if (lzp == "0") { print "lzpos=0" } else { print "lzpos=" ((lzp - 1 ) / 2 ) + 8 "; lzsign=" substr($0,lzp,16) ";" } }')
		grub2ver=L${lzsign} ; grub2ver=${!grub2ver}

		if [ $lzpos -gt 0 ]; then
			case ${grub2ver} in
			"1.99")
				eval $(hexdump -v -s 520 -n 13 -e '1/4 "m=%u; " 1/4 "k=%u; " 1/4 "csize=%u; " 1 "prt=%d;"' $core)
			 	[ $(hexdump -v -s ${lzpos} -n 8 -e '1/1 "%02x"' $core) = '0000000000000000' ] && lzpos=$(($lzpos + 8))
		      	mod_info=$(($k - $lzpos + 512))
				lzusize=$(($m + $mod_info))
				lzcount=$(($lzusize/$lzpos + 1))
				printf '\x5d\x00\x00\x01\x00'$(printf '%08x' $((512 - $lzpos)) | awk '{printf "\\x%s\\x%s\\x%s\\x%s", substr($0,7,2), substr($0,5,2), substr($0,3,2), substr($0,1,2)}')'\x00\x00\x00\x00' > $clog
				dd if=$core bs=$lzpos skip=1 count=$lzcount 2> /dev/null | cat $clog - | $unlzma > $ucore 2>/dev/null
		      	cdir=$(hexdump -v -n 64 -e '"%_c"' $ucore)
		      	cdir="${cdir%%\\0*}"
		      	eval $(hexdump -v -n 12 -s $mod_info -e '"mod_magic=" 4/1 "%_c" 1/4 "; mod_pos=%u; " 1/4 "mod_size=%u;"' $ucore)
       		   	if [ "x${mod_magic}" == "xmimg" ] ; then
 					mod_end=$(($mod_info + $mod_size));
 					mod_head=$(($mod_info + $mod_pos));
					p=${mod_head}
					while [ ${mod_head} -lt ${mod_end} ] ; do
			   			eval $(hexdump -v -n 8 -s $mod_head -e '1/4 "mod_type=%u; " 1/4 "mod_size=%u;"' $ucore)
			        	if [ ${mod_type} -eq 2 ]; then
							embedded=1 
			      			cdir=$( hexdump -v -n $(( ${mod_size} - 8 )) -s $(( ${mod_head} + 8 )) -e '"%_c"' $ucore)
			      			cdir=$( hexdump -v -n $(( $mod_size - 8 )) -s $(( $mod_head + 8 )) -e '"%_c"' $ucore );
				           	cdir=$(printf "${cdir%\\0}")
							if [ "$cdir" ]; then	
								eval $(echo $cdir | sed 's|.*\(fs_uuid\) \([^ ]*\).*\(root\) \([^ ]*\).*|\1=\"\2\";\3dev=\"\4\";|')
								if [ "$fs_uuid" -a  "$(which blkid 2>/dev/null)" ]; then
									p=$(blkid -U $fs_uuid 2>/dev/null)
									p=${p##*/}
								elif [ "$rootdev" ]; then
									eval "p=$(echo $rootdev | sed 's|\(.*\),[a-z]*\([0-9]*\).*|${\1}\2|')"
								fi
								[ "$p" ] &&  [ -b /dev/$p ] || p="$dsk?"
								[ "$p" ] &&	break
							fi
						else
							p=${dsk}$(hexdump -v -s 7 -n 8 -e '"%_c"' $ucore | sed 's|)/boot.*||')
						fi
			       		mod_head=$(($mod_head + $mod_size))
			       done
				fi
				;;
			"2.00")	
	        	eval $(hexdump -v -s 520 -n 20 -e '1/4 "csize=%u; " 1/4 "lzusize=%u; " 1/4 "rsr=%u; " 1/4 "rsl=%u; bdev=" 3/1 "%x" 1 "; bdrv=%d;"' $core);
				[ $(hexdump -v -s ${lzpos} -n 4 -e '1/1 "%02x"' $core) = '00000000' ] && lzpos=$(($lzpos + 4))
				lzcount=$lzusize
	        	printf '\x5d\x00\x00\x01\x00'$( printf '%08x' $lzusize | awk '{printf "\\x%s\\x%s\\x%s\\x%s", substr($0,7,2), substr($0,5,2), substr($0,3,2), substr($0,1,2)}' )'\x00\x00\x00\x00' > $clog
				dd if=$core bs=$lzpos skip=1 count=$lzcount 2> /dev/null | cat $clog - | $unlzma > $ucore 2>/dev/null
	
	        	eval $(hexdump -v -s 19 -n 4 -e '1/4 "mod_info=%u;"' $ucore)
	        	eval $(hexdump -v -n 12 -s $mod_info -e '"mod_magic=" 4/1 "%_c" 1/4 "; mod_pos=%u; " 1/4 "mod_size=%u;"' $ucore)

       		   	if [ "x${mod_magic}" == "xmimg" ] ; then
	 				mod_end=$(($mod_info + $mod_size));
	 				mod_head=$(($mod_info + $mod_pos));
					p=${mod_head}
					while [ ${mod_head} -lt ${mod_end} ] ; do
			       		eval $( hexdump -v -n 8 -s $mod_head -e '1/4 "mod_type=%u; " 1/4 "mod_size=%u;"' $ucore)
			            if [ ${mod_type} -eq 2 ]; then 
							embedded=1 
			           		cdir=$(hexdump -v -n $(( ${mod_size} - 8 )) -s $(( ${mod_head} + 8 )) -e '"%_c"' ${ucore})
				           	cdir=$(printf "${cdir%\\0}")
							if [ "$cdir" ]; then	
								eval $(echo $cdir | sed 's|.*\(fs_uuid\) \([^ ]*\).*\(root\) \([^ ]*\).*|\1=\"\2\";\3dev=\"\4\";|')
								if [ "$fs_uuid" -a  "$(which blkid 2>/dev/null)" ]; then
									p=$(blkid -U $fs_uuid 2>/dev/null)
									p=${p##*/}
								elif [ "$rootdev" ]; then
									eval "p=$(echo $rootdev | sed 's|\(.*\),[a-z]*\([0-9]*\).*|${\1}\2|')"
								fi
								[ "$p" ] &&	break
							fi
			            elif [ ${mod_type} -eq 3 ]; then 
			            	cdir=$(hexdump -v -n $(( $mod_size - 8 )) -s $(( $mod_head + 8 )) -e '"%_c"' $ucore)
			            	cdir=$(printf "${cdir%\\0}")
							p=${dsk}$(echo $cdir | sed 's|(,[a-z]*\([0-9]*\)).*|\1|')
						fi
			            mod_head=$(($mod_head + $mod_size))
			       done
				fi
			;;
			esac

			[ "$p" ] || p="sd?"
			echo $p $lzusize
		else
			echo "sd??"
		fi
		rm -f $ucore $clog
	else
		echo "sd??"
	fi
}

function coreimg {
	INVALID=""
	grubver="$1"; shift
	dev=${1:0:8}
	COREPOS=$(hexdump -v -s 92 -n 4 -e '"%u"' $1)
	DRIVENR=$(($(hexdump -v -s 100 -n 1 -e '"0x%02x"' $1)-127))
	if [ "$DRIVENR" == "128" ] ; then
		dsk=${dev##*/}
	else
		h=hd$(($DRIVENR - 128))
		dsk=${!h}
	fi
	COREDEV="sd??"
	ST2POS=$COREPOS
	if [ $COREPOS -eq 1 ]; then	# core.img is embedded
		magic=$(hexdump -v -s 512 -n 4 -e '/1 "%02x"' $1)
		[ "$magic" == "$gmagic199" -o "$magic" == "$gmagic200" ] || return
		if [ "$grubver" == "1.99" ]; then
			[ "$UNLZMA" ] || { 
				COREDEV=$(lzmacore $dsk $dev $COREPOS)
				CORESIG=${COREDEV#* };	COREDEV=${COREDEV% *}
		}
		else
			p=$(($(hexdump -v -s 1044 -n 1 -e '"%d"' $1)+1))
			COREDEV=${dsk}$p
		fi
	else # core.img is not embedded
		if [ "$grubver" == "1.99" ]; then
			[ "$UNLZMA" ] || { 
				COREDEV=$(lzmacore $dsk $dev $COREPOS)
				CORESIG=${COREDEV#* }; COREDEV=${COREDEV% *}
			}
		else
			dd if=$dev bs=512 count=1 skip=$COREPOS 2>/dev/null | grep -q loading || INVALID="INVALID"
			p=$(dd if=$dev bs=512 count=1 skip=$(($COREPOS+1)) 2>/dev/null | hexdump -v -n 64 -e '"%_u"'| sed "s|.*(,\(.*\)|\1|;s|)||;s|nul||g;s|msdos\([1-9][0-9]*\)/.*|\1|")
			if [ ${#p} -gt 2 ]; then
				INVALID="INVALID"
				COREDEV="${dsk}?"
			elif [ $(($p*1)) -eq 0 ]; then
				INVALID="INVALID"
				COREDEV="${dsk}?"
			else
				COREDEV=${dsk}$p
				[ -b /dev/$COREDEV ] || INVALID="INVALID"
			fi
		fi
	fi

	if [ "$CORESIG" ]; then
		[ "$CORESIZE" ] && printf " <%s> " $CORESIG
		CORESIG=CS$CORESIG; CORESIG="${!CORESIG};"
	fi
	echo $CORESIG$COREDEV:${!COREDEV}@${ST2POS}${INVALID}
}

function checkGrub {
	D=$1
	dd if=$1 bs=512 count=1 2>/dev/null | grep -q GRUB || return 1
	G="GV$(hexdump -v -s 128 -n 2 -e '/1 "%x"' $1)" ; GRUB=${!G}
	BS=$(dd if=$1 bs=512 count=1 2>/dev/null | tr -c "[:alnum:]" "0")
	POS=${BS%GRUB*} ; POS=${#POS}
	SIGN=G$POS ; SIGN=${!SIGN}

	[ "x$SIGN" == "x" ] && SIGN=$(printf "0x%x" $POS)
	COL=C$POS ; COL=${!COL} ; COL=${COL:-$C999}
	case $GRUB in
	Grub)
		ST2=$(stage2 $D); GRCOL=$whi
		GRUB="Legacy GRUB"
		HOW="${whi}using stage1.5"
	;;
	Grub2)
		GRUB2VER=${G}_ver; GRUB2VER=${!GRUB2VER}
		ST2=$(coreimg "$GRUB2VER" $D); GRCOL=$yel
		GRUB="$GRUB ($(Grub2ver $GRUB2VER))"
		CS=${ST2%;*}
		[ "$CS" == "$ST2" ]	|| SIGN=${CS:-$SIGN}
		ST2=${ST2#*;}
		[ "$GRUB2VER" == "1.99" -a "$UNLZMA" ] && LZMAWARN="unlzma not found. Unable to read compressed Grub core."
		HOW="${whi}using core"
	;;
	esac

	[ "${ST2%%INVALID}" == "$ST2" ] || SIGN="INVALID"
	ST2=${ST2%%INVALID}; PST2=${ST2/:*@/ }
	ST2POS=${ST2##*@}; ST2=${ST2%%@*}; FS=${ST2##*:}; ST2=${ST2%%:*}
	[ "$FS" == "${FS##*sd}" ] && FS="0x$FS" || FS="=> $FS?"
	[ "$FS" == "0x" ] && FS="0x??"
	if [ "$ST2" == "NOFOUND" ]; then
		ST2="Drive not found!"
		FS=""; SIGN=""
	else
		SIGN="($SIGN)"
	fi
	[ ${#D} -eq 8 ] && D="$(basename $D) MBR" 
	if [ $ST2POS -gt 1 ]; then
		[ "$SIGN" == "(INVALID)" ] && HOW="at offset ${red}$ST2POS" || HOW="at offset ${blu}$ST2POS"
	fi
	[ "$ST2" ] && ARROW=" =>" && ST2=" $ST2"
	if [ "$VERBOSE" ]; then
		printf " --> %s%-12s %sfound in %s%-10s%s %s%s%-6s  %s%s %s %s%s%s" "$GRCOL" "$GRUB" "$sgr" "$red" "$D" "$sgr" "$ARROW" "$yel" "$ST2" "$sgr" "$FS" "$HOW" "$COL" "$SIGN" "$sgr" 
		[ "$SHOWCORE" ] && showCore $PST2
	else
		printf " --> %s%-12s %sfound in %s%-10s%s %s%s%-6s  %s%s %s%s%s" "$GRCOL" "$GRUB" "$sgr" "$red" "$D" "$sgr" "$ARROW" "$yel" "$ST2" "$sgr" "$FS" "$COL" "$SIGN" "$sgr"
	fi
	return 0
}

function checkMBR {
	D=$1
	SIG=$(hexdump -v -s 510 -n 2 -e '"%04x"' $D)
	if [ "x$SIG" != "xaa55" ]; then
		printf " --> %sInvalid boot signature: 0x%s%s" "$red" "$SIG" "$sgr"
		return 2
	else
		SIG=$(hexdump -v -n 2 -e '"%02x"' $D)
		M=M$SIG; M=${!M}
		if [ "$M" ]; then
			DS=$(hexdump -v -s 440 -n 4 -e '/2 "%02x"' $D)
			printf " --> %s%s %s(Sig: %s0x%s%s)" "$cya" "$M" "$sgr" "$red" "$DS" "$sgr"
			return 1
		else
			checkGrub $1
		fi
	fi
}

function addGrubPrimaryPart {
[ ${1##*[a-z]} -le 4 ] && grubpart="$grubpart $1"
}

function setBootFlag {
if [ "$grubpart" ]; then
	for part in $grubpart; do
		num=${part##*[a-z]}; dev=${part%%[1-4]*}
		$fdisk -l $dev | grep "$part\b" | grep -q "*" && printf "\n %sGrub partition %s is already active.%s\n" "$red" "$part" "$sgr" && continue	
		while [ "true" ]; do
			printf "\n %sGrub found in %s. Do you want to activate %s? [yn]%s " "$red" "$part" "$part" "$sgr"
			read -n1 yesno
			yesno=$(echo $yesno | tr "[:upper:]" "[:lower:]")
			[ "$yesno" == "y" -o "$yesno" == "n" ] && break
		done
		printf "\n"
		if [ "$yesno" == "y" ]; then
			if [ -x $sfdisk ]; then
				$sfdisk $dev -A$num
			else
				printf "%s%s not found%s\n" "$red" "$sfdisk" "$sgr"
				return 1
			fi
		fi
		printf "\n"
	done
else
	printf "\n %sBootflag can not be set: No Grub was found in primary or in the extended partition.%s\n" "$red" "$sgr"
fi
}

# Main - BEGIN --------------------------
unlzma="xz --format=lzma --decompress"
which xz &>/dev/null || UNLZMA=no
devs=(`LC_ALL=C $fdisk -l 2>/dev/null | sed -n 's|^Disk \(/dev/[hsv]d[a-z]\):.*|\1|p' | sort -V`)

args=`getopt -q -u -o hwkadsvcmMn -l help,writemenu,kernel,activate,debug,coresize,verbose,core,map,maponly,nocolor -- "$@"`

set -- $args
for i; do
case "$i" in
	-h|--help)		HELP=1; shift ;;
	-w|--writemenu)	WRITEMENU=1; shift ;;
	-k|--kernel)	SEARCHKERNEL=1 ; shift ;;
	-a|--activate)	BOOTFLAG=1; grubpart=""; shift ;;
	-d|--debug)		DEBUG=1; shift ;;
	-s|--coresize)	CORESIZE=1; shift ;;
	-v|--verbose)	VERBOSE=1; shift ;;
	-c|--core)		VERBOSE=1; SHOWCORE=1; shift ;;
	-m|--map)		DBG="--- DEVICE.MAP:"; shift ;;
	-M|--maponly)	DBG="--- DEVICE.MAP:"; MAPONLY="yes"; shift ;;
	-n|--nocolor)	NOCOLOR=1; shift ;;
esac
done

[ "$NOCOLOR" ] && unset red gre yel blu mag cya whi dred dgre dyel dblu dmag dcya dwhi sgr
[ "$HELP" ] && help

# Some distros colors
declare C999 C377 C383 C385 C384 C392
C999=$yel
C392=$mag
C384=$cya
C377=$red
C383=$cya
C384=$gre
C385=$gre

# some partition types with color

x6="FAT16:$yel"
x16="Hidden FAT16:$yel"
xb="FAT32:$gre"
xc="FAT32:$gre"
xe="FAT32:$gre"
x1b="Hidden FAT32:$gre"
x1c="FAT32 Win RE:$gre"
x1e="Hidden FAT32:$gre"
x5="Extended:$gre"
xf="Extended:$gre"
x7="NTFS:$blu"
x17="Hidden NTFS:$dred"
x27="NTFS Win RE:$dred"
x82="swap:$dyel"
x83="LINUX:$cya"
x8e="LINUX LVM:$cya"
xee="GPT:$dred"
xef="EFI:$dred"
xaf="HFS:$dmag"
xa5="FreeBSD:$dmag"
xa6="OpenBSD:$dmag"
xa7="Next Step:$dmag"
xa8="Darwin:$dmag"
xa9="NetBSD:$dmag"
x39="Plan9:$dcya"
x4d="QNX4:$dcya"
x4e="QNX4:$dcya"
x4f="QNX4:$dcya"
x63="GNU HURD:$dcya"
x81="MINIX:$dcya"
xbe="Solaris boot:$dcya"
xbf="Solaris:$dcya"
xeb="BeOS:$dcya"
xfb="VMware VMFS:$dcya"
xfc="VMware VMKCORE:$dcya"
xfd="LINUX RAID AUTO:$dcya"

# look for GRUB signature in MBR, Linux(0x83) and Extended(0x05, 0x0f) partitions
# skip EFI (0xee), swap (0x82) and BSD (0xa5, 0xa6, 0xa9) partitions
# !!! deliberately skip Windows Recovery (0x27) partition

# look for strings 'BOOTMGR' or 'NTLDR' in all other partitions bootsector and
# if found mount the partition and search for files bootmgr or ntldr

# in debug mode, output bootsector of NTFS and FAT partitons in ASCII and HEX
# output partition table

# get all partitions ids
eval `LC_ALL=C $fdisk -l 2>/dev/null | awk '/^\/dev/ { if ($2 == "*") ID = $6; else ID = $5; print $1"="ID";" }' | sed s'|/dev/||' | sort -V`
eval `LC_ALL=C $fdisk -l 2>/dev/null | awk '/^\/dev/&&/*/ {sub(/\/dev\//,"",$1); print $1"bf=*" }' | sort -V`

# map BIOS drive to Linux devices
eval $(for dev in ${devs[*]}; do [ -b $dev ] && udevadm info --query=property --name=$(basename $dev) 2>/dev/null | awk -F "=" '/DEVPATH/ { sub(/.*target/, "", $2) ; sub(/:.*\//, " ", $2) ; print $2 }' ; done | sort | awk '{ I=NR -1 ; printf "%s=hd%s; hd%s=%s; ", $2, I, I, $2 }')

for dev in ${devs[*]}; do
	if [ -b $dev ]; then
		sd=${dev##*/}; hd=${!sd}
		eval $($udevadm info --query property --name=$dev | awk -F "=" '/ID_BUS|ID_TYPE/{ print $0, ";" }')
		[ "x$ID_TYPE" == "xfloppy" ] && continue 

		sdsk=$($udevadm info --query=property --name=$dev | awk -F "=" '/DEVLINKS|DEVPATH/ { print $2 }')

		[ "$DBG" ] && printf "\n%s %s%s %sis %s drive %s%s%s" "$DBG" "$gre" "$sd" "$sgr" "$ID_BUS" "$whi" "$hd" "$sgr"

		if [ -f $devmap ]; then
			[ "$DBG" ] && printf "\n%s looking for %s%s %sin %s:" "$DBG" "$gre" "$dev" "$sgr" "$devmap"
			unset hdmap
			[ "x$ID_BUS" == "xusb" ] && SETTO="" || SETTO=" - set to"
			found="- ${red}missing in device.map${noc}${SETTO}"
			for d in $dev $sdsk; do
				str="$d"
				grep -q "$d" $devmap && {
					hdmap=$(sed '/hd[0-9]*,/d' $devmap | grep "$d" | awk '{ print $1 }' | tr -d "()")
					str="${cya}$str ${noc}-> ${whi}$hdmap$noc"
					found="- found in device.map - is now"
				}
				[ "$DBG" ] && printf "\n%s%s - %s" "$sgr" "$DBG" "$str"
			done
			if [ "$sdsk" ]; then [ "$hdmap" ] && eval "$sd=$hdmap;$hdmap=$sd" || NOMAP="${NOMAP} $dev"; fi
		else
			[ "x$ID_BUS" == "xusb" ] && found="" || found="might be BIOS drive"
			for d in $dev $sdsk; do [ "$DBG" ] && printf "\n%s - %s" "$DBG" "$d"; done
		fi

		[ "$DBG" ] && printf "\n%s => %s%s%s %s %s%s\n%s%s" "$DBG" "$gre" "$sd" "$sgr" "$found" "$whi" "${!sd}" "$sgr" "$DBG"

		# get startsector of all partitions
		S=S$sd
		SECS=$($fdisk -u -l $dev 2>/dev/null | tr -d "*" | awk '/^\/dev\// { sub(/\/dev\//,"",$1) ; printf "%s:%s ", $2, $1 }')
	 	[ "$SECS" ] && eval $(echo declare "$S='$SECS'")
	fi
done

if [ "$DBG" ]; then
	[ -f $devmap ] && checkDevmap
	hr n \*
	[ "$MAPONLY" ] && hr N && exit
fi

for dev in ${devs[*]} ; do
	if [ -b $dev ] ; then

		GPT=$(hexdump -v -s 450 -n 1 -e '"%x"' $dev)
		MBR="MBR"
		if [ "$GPT" == "ee" ]; then
			if [ "$(hexdump -v -s 466 -n 1 -e '"%x"' $dev)" == "0" ]; then
				printf "\n - skipping protective MBR on disk %-30s ..." "$dev" ; continue
			else
				MBR="hybrid MBR"
			fi
		fi

		printf "\n%s - reading %s on disk %-30s %s..." "$red" "$MBR" "$dev" "$sgr"
		checkMBR $dev
		for pt in `LC_ALL=C $fdisk -l $dev 2>/dev/null | awk '/^\/dev/ { print $1 }'`; do
			PT=$(basename $pt); fs=${!PT}
			bf="${PT}bf"; bf=${!bf}
			f=x${fs}; f=${!f}; FS=${f%:*}; CL=${f#*:}
			if [ "$DEBUG" ]; then
				case $fs in
					6|b|c|e|7) printbs $pt ;;
				esac
			else
				case $fs in
				0) ;;
				1b|1c|1e|ee|af|a5|a6|a9|82|27)
				printf "\n - skipping partition  %s%-11s %-2s %-15s%s" "$CL" "$pt" "$bf" "($FS)" "$sgr" ;;
				5|f)
				printf "\n - reading bootsector  %s%-11s %-2s %-15s %s..." "$CL" "$pt" "$bf" "($FS)" "$sgr"
				checkGrub $pt && addGrubPrimaryPart $pt ;;
				83)
				printf "\n - reading bootsector  %s%-11s %-2s %-15s %s..." "$CL" "$pt" "$bf" "($FS)" "$sgr"
				checkGrub $pt && addGrubPrimaryPart $pt 
				[ "${SEARCHKERNEL}" ] && searchkernel $pt ;;
				*)
				printf "\n - searching partition %s%-11s %-2s %-15s %s..." "$CL" "$pt" "$bf" "($FS)" "$sgr"
				dd if=$pt bs=512 count=1 2>/dev/null | grep -q NTLDR && findbootmgr $pt ntldr $fs
				dd if=$pt bs=512 count=1 2>/dev/null | grep -q BOOTMGR && findbootmgr $pt bootmgr $fs ;;
				esac
			fi
		done
		hr N
		[ "$DEBUG" ] && printptbl $dev
	fi
done

hr N

[ "$NOMAP" ] && devmapWarning 1 $NOMAP
[ -f $devmap ] || devmapWarning 2 

[ "$LZMAWARN" ] && unlzmaWarning

# activate Grub partition if option -a ( or --activate ) was given
[ "$BOOTFLAG" ] && setBootFlag

[ -d /findgrub ] && rmdir /findgrub

# Main - END ---------------------------
echo
read -p "Press <enter> to Exit findgrub..."

exit 0
# End Of Script
