#!/bin/bash
# list disks - lsdisk
PGMNAME=`basename ${0}`
VERSION='0.15';
RELEASE_DATE='Sep. 20, 2012';
###########################################################################################################
#     copyright 2012 Wilfried Stenzel <wilstet@gmail.com>
#     This program is free software: you can redistribute it and/or modify
#     it under the terms of the GNU Affero General Public License as
#     published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
#     You should have received a copy of the GNU Affero General Public License
#     along with this program.  If not, see <http://www.gnu.org/licenses/agpl-3.0>.
#
############################################################################################################
#***********************************************************************************************************
#  The program creates a list of attached disks without root privileges.
#  Content: devicename, start-,end- and  sector size; label and UUID; filesystem and mountpoint (if mounted).
#  It does not contain bash specific code, so it should work on other shells too (tested: bash,dash,zsh,ksh,csh).
#
#  SysFS is a kernel filesystem, placed in /sys, starting with kernel 2.6. (2.6.31 or later works) This
#  filesystem represent information in the userspace.  lisdisk relies on matching this information:
#  ATTACHED DISKS:/proc/partitions				all devices connected
# 		  /proc/self/mountstats				all devices mounted
# 		  /dev/disk/by-path				for number of partitions
# 		  /dev/disk/by-uuid				for the uuid
# 		  /dev/disk/by-label				for the label
#  DEVICE:        /sys/block/<device>/
# 		  /sys/block/<device>/size			sectorsize
# 		  /sys/block/<device>/queue/logical_block_size	logical blksize
# 		  /sys/block/<device>/queue/physical_block_size	physical blksize
#  PARTITION:     /sys/block/<device>/<partition>/
#	 	  /sys/block/<device>/<partition>/size		partition-sectorsize
#	 	  /sys/block/<device>/<partition>/start		partition-startsector
# Boot-Image:     /proc/cmdline
# EFI or BIOS:    /sys/firmware/efi                             partition with efivars
#***********************************************************************************************************
###########################  Definitions ################################
Bit1000="B KB MB GB TB PB EB ZB YB"          # For calculating size  -SI decimal prefixes
Bit1024="B KiB MiB GiB TiB PiB EiB ZiB YiB"  # For calculating size - IEC binary prefixes
maxlogicalKB=8                               #  max. logical sector size for search GPT (8KiB)
OptRoot=0                                    # root        = No
OptBoot=0                                    # Option -k   = No
OptList=0                                    # Option -l   = No
OptDisk=0                                    # Option -f/g = No
OptParted=0                                  # Option -b/s = No
OptAnalyse=0                                 # Option -A   = No
Options=$@                                   # Save all Opions
DevDSN=''                                    # Start list of all <devices> to be processed
#########################################################################

usage() {
    cat <<EOF
 $PGMNAME $Options
          $*
        Usage: lsdisk <[options]> <device>

        Device: not specified - all disk stored in /proc/partitions
                 if specified - they must be registered in /dev/

        Options:
                no options                     List device information
                -a  --analyse	         root: Analyze/show the partition table
                -l  --list                     List device information
                -h  --help                     Show this help message
                -v  --version                  Display version information

        Special goodies:
        If gparted or fdisk/gdisk (depending on partition type) are installed you can also invoke them with lsdisk.
        The following options are supported:
                -b  --parted-bytes       root: List with "parted <device> print"
                -d  --disk               root: List partition information, depending on the Partition Table with "fdisk / gdisk"
                -s  --parted-sectors     root: List with "parted <device> unit s print"$*
$*
EOF
 exit 1
}

##################################################################################
##                  O P T I O N S                                               ##
_short_opts () {
OPTIND=1
 while getopts ':aAbdhlpvs' OPTION ; do
  case "$OPTION" in
    a | A ) OptAnalyse=1                     ;;     # A for older version
    b     ) OptParted=1;PartedPrint=''       ;;
    d     ) OptDisk=1                        ;;
    l | p ) OptList=1		         ;;     # p for older version
    s     ) OptParted=1;PartedPrint='unit s' ;;
    h     ) usage 		                 ;;
    v     ) echo "$PGMNAME - $VERSION - $RELEASE_DATE" ;  exit 0 ;;
    ?     ) usage "  >>> invalide options "          ;;
  esac
done
}

_long_opts () {
OPTION=$(echo $1 | cut -d '=' -f1)
OPTARG=$(echo $1 | cut -d '=' -f2)
case "$OPTION" in
   --analyse        )  OptAnalyse=1                     ;;
   --parted-bytes   )  OptParted=1;PartedPrint=''       ;;
   --disk           )  OptDisk=1                        ;;
   --list | print   )  OptList=1                        ;; # print for a older version
   --parted-sectors )  OptParted=1;PartedPrint='unit s' ;;
   --help           )  usage                            ;;
   --version        )  echo "$PGMNAME - $VERSION - $RELEASE_DATE" ;  exit 0 ;;
   *                ) usage "  >>> invalide options "              ;;
esac
}

_device_opts () {
for dsn in $@ ; do
    dsn=$(basename $dsn 2>/dev/null)
    [ ! -b /dev/${dsn} ] && usage "  >>> invalid or no valid device: $@"
                # only devices no partitions
    DevDSN="$DevDSN $(echo $dsn|sed -n 's/\([a-zA-Z]\+\).*$/\1/p')"
    shift
done
}

for opts in $@ ; do
 case $(expr match "$opts" '[\-]*') in
   0 ) _device_opts "$opts" ;;
   1 )  _short_opts "$opts" ;;
   2 ) _long_opts "$opts" ;;
   * ) usage "  >>> invalide options ";;
 esac
done

##################################################################################
##                          P R O G R A M S                                     ##
which expr >/dev/null || pgm=$pgm' expr'
which cat >/dev/null  || pgm=$pgm' cat'
which sed >//dev/null || pgm=$pgm' sed'
which grep >/dev/null || pgm=$pgm' grep'
which find >/dev/null || pgm=$pgm' find'
which dmidecode >/dev/null || pgm=$pgm' dmidecode'
grep -q "/sys with " /proc/self/mountstats  || pgm=" /sys (sysfs - kernel 2.6.31 or later)"
grep -q "/proc with " /proc/self/mountstats || pgm=$pgm"  /proc (proc)"

[ -f /etc/lsb-release ] && . /etc/lsb-release    # is there a ubuntu-style distro ?

[ $(whoami) = root ] && OptRoot=1

if [ $OptRoot -eq 0 ]; then
   [ $OptParted != 0 -o $OptDisk != 0 -o $OptAnalyse != 0 ] && usage ">>> you need root privileges for these options"
else
##   P R O G R A M S needed when root and Option b/d/s   ##
   which hexdump >/dev/null 	  || pgm=$pgm' hexdump'
   partprobe=$(which partprobe || echo "< >")
   fdisk=$(which fdisk         || echo "< >")                            # if no fdisk found
    [ "$fdisk" = "< >" ]      && { OptParted=1; PartedPrint="unit s" ;}  #   then use parted sectors
      gdisk=$(which gdisk         || echo "< >")                         # if no gdisk found
    [ "$gdisk" = "< >" ]      && { OptParted=1; PartedPrint="unit s" ;}  #   then use parted sectors
      parted=$(which parted       || echo "< >")                         # if no parted found then
    [ "$parted" = "< >" ]     && OptParted=0                             #   then clear OptParted
fi

[ x"$pgm" != "x" ] && usage ">>> Programs not installed: $pgm"

##################################################################################
##        when no <device>  then get /proc/partitions
  [ x"$DevDSN" = "x" ] && DevDSN=$(sed -ne 's/^.*[0-9\+].*[0-9\+].*[0-9\+] \([a-zA-Z]\+$\)/\1/p' /proc/partitions | tr -s '\n' ' ')

##        little helper
  work=$(expr $OptList + $OptDisk + $OptParted + $OptAnalyse)
  [ $work -eq 0 ]     && { OptList=1; Options="-l $DevDSN $PartDSN" ;}   #  no root & no options = OptList


##################################################################################
##        proof: EFI or BIOS
[ -d /sys/firmware/efi ] && mboard="EFI-loaded" || mboard="BIOS-loaded"       # if directory then EFI
[ $OptRoot -eq 1 ] && dmidecode -t0 | grep -iq efi && mboard=$mboard"(EFI)"   # if root then proof board


##################################################################################
_get_devinfo () {
##        <device> information (sysfs)
local work
model=
partcnt=
  partcnt=$(find /dev/disk/by-path -lname ../../${dev}[0-9]\* |wc -l)     # count the numbers of partitions
  work=$(cat /sys/block/$dev/device/vendor 2>/dev/null |tr -s ' ' )       # get vendor and
  model=$(cat /sys/block/$dev/device/model 2>/dev/null | tr -t ' ' '_')   #     model name
  model=$(echo "$work $model" | tr -t ' ' '_')                            # set model and change blank to _
  # get logical/physical size - if blank set 0
  logicalsize=$(grep '[0-9+]$' /sys/block/$dev/queue/logical_block_size 2>/dev/null   || echo 0)
  physicalsize=$(grep '[0-9+]$' /sys/block/$dev/queue/physical_block_size 2>/dev/null || echo 0)
  devsectors=$(grep '[0-9+]$' /sys/block/$dev/size 2>/dev/null || echo 0) # get total sectors - if blank set 0
}

##################################################################################
_analyse_mbr() {
##        <device> Partition Table: type and id
local gpt1 gpt2 tabid1 tabid2 tabid3 tabid4

[ $OptRoot -ne 1 ] && return         # OptRoot?
work=$(echo $partprobe | grep "partprobe")   # partprobe installed?
[ x"$work" != "x" ] && partname=$(partprobe -ds /dev/${dev} | cut -d ' ' -f2) #    then get the tablename (for fdisk/gdisk)

tabid1=$(hexdump -s450 -n1 -e '1/1 "%02x"' /dev/$dev 2>/dev/null)    # get the partition-id entry 1
[ x"$partname" = "x" ] && { [ "$tabid1" = "ee" ] && partname="gpt" || partname="msdos?" ;}
[ "$partname" = "gpt" ] && parttable="$partname/$tabid1" || parttable=$partname  # set parttable for listing

[ $OptAnalyse = 0 ] && return     # OptAnalyse?

tabid2=$(hexdump -s466 -n1 -e '1/1 "%02x"' /dev/$dev 2>/dev/null)   # get the partition-id of the entries 2-4
tabid3=$(hexdump -s482 -n1 -e '1/1 "%02x"' /dev/$dev 2>/dev/null)   #   for check Hybrid-GPT
tabid4=$(hexdump -s498 -n1 -e '1/1 "%02x"' /dev/$dev 2>/dev/null)

# check for EFI PART - first two LBAs and last LBA
maxlogicalKB=$(expr $maxlogicalKB \* 1024)  # max. bytes for scan
gpt1=0
        # check for EFI PART primary - $maxlogicalKB   get offset (decimal); calculate the positions
gpt1=$(printf '%d' 0x0$( hexdump -n$maxlogicalKB -C /dev/$dev | grep "EFI PART"|cut -d ' ' -f1))
        # check for EFI PART secondary - set LBA = device sectors - 1  or 0 (with dd=ist faster)
gpt2=$(dd if=/dev/$dev skip=$(expr $devsectors - 1) 2>/dev/null|hexdump | grep -q "4645 2049 4150 5452" && echo $(expr $devsectors - 1) || echo 0)

if [ "$tabid1" = "ee" -o "$tabid1" = "ef" ]; then   # Hybrid-GPT?
     [ "$tabid2$tabid3$tabid4" = "000000" ] && parttyp="gpt" || parttyp="gpthybrid"  # get the parttyp-type for listing partition-entry with hexdump
else parttyp="legacymbr"                  # Legacy-MBR
     [ $gpt1 -ge 1 ] && gpt="LBA $gpt1"         # Is there something of EFI PART?
     [ $gpt2 -ge 1 ] && gpt="$gpt / LBA $gpt1"  #   set info for listing
fi
}

##################################################################################
_analyse_gpt() {
##          <device> Analyse GPT-Partition Table
local primary secondary      # get the EFI-Header-Entries (92 Bytes) and format them for cut (del :)
primary=$(hexdump -s${logicalsize} -n92 -ve '"\n" 8/1 "%c" /2 ":%04x" /1 "%02x" /1 "%02x:" /4 "%d:" /4 "%08x:" /4 "%08x:" 8/1 " %02x " " 0x :" 8/1 " %02x " " 0x : " 8/1 " %02x " " 0x :" 8/1 " %02x " " 0x :" /4 "%08X-" /2 "%04X-" /2 "%04X" "-" 2/1 "%02X" "-" 6/1 "%02X" ":" 8/1 " %02x " " 0x :"  1/4 "%d:" 1/4 "%d:" /4 "%08x" "\n\n"' /dev/$dev)
           # split the EFI-Header-Entries and calculat the LBAs
prim_sign=$(echo "$primary" | cut -d':' -f1|tr -d '\n')
prim_rev=$(echo "$primary"  | cut -d':' -f2)
prim_size=$(echo "$primary" | cut -d':' -f3)
prim_hcrc=$(echo "$primary" | cut -d':' -f4)
prim_res=$(echo "$primary"  | cut -d':' -f5)
prim_current=$(printf "%d" $(echo "$primary" | cut -d':' -f6  | tac -s' '| tr -d ' '))
prim_backup=$(printf "%d" $(echo "$primary" | cut -d':' -f7   | tac -s' '| tr -d ' '))
prim_firstuse=$(printf "%d" $(echo "$primary" | cut -d':' -f8 | tac -s' '| tr -d ' '))
prim_lastuse=$(printf "%d" $(echo "$primary" | cut -d':' -f9  | tac -s' '| tr -d ' '))
prim_guid=$(echo "$primary" | cut -d':' -f10)
prim_partstart=$(printf "%d" $(echo "$primary" | cut -d':' -f11 | tac -s' '| tr -d ' '))
prim_partmax=$(echo "$primary" | cut -d':' -f12)
prim_partsize=$(echo "$primary" | cut -d':' -f13)
prim_pcrc=$(echo "$primary" | cut -d':' -f14)

## get the EFI-Backup-Entries and format them for cut (del :)
secondary=$(dd if=/dev/$dev skip=$prim_backup count=1 2>/dev/null|hexdump -n92 -ve '"\n" 8/1 "%c" /2 ":%04x" /1 "%02x" /1 "%02x:" /4 "%d:" /4 "%08x:" /4 "%08x:" 8/1 " %02x " " 0x :" 8/1 " %02x " " 0x : " 8/1 " %02x " " 0x :" 8/1 " %02x " " 0x :" /4 "%08X-" /2 "%04X-" /2 "%04X" "-" 2/1 "%02X" "-" 6/1 "%02X" ":" 8/1 " %02x " " 0x :"  1/4 "%d:" 1/4 "%d:" /4 "%08x" "\n\n"')
           # split the EFI-Backup-Entries and calculat the LBAs
sec_sign=$(echo "$secondary" | cut -d':' -f1|tr -d '\n')
sec_rev=$(echo "$secondary" | cut -d':' -f2)
sec_size=$(echo "$secondary" | cut -d':' -f3)
sec_hcrc=$(echo "$secondary" | cut -d':' -f4)
sec_res=$(echo "$secondary" | cut -d':' -f5)
sec_current=$(printf "%d" $(echo "$secondary" | cut -d':' -f6|tac -s' '|tr -d ' '))
sec_backup=$(printf "%d" $(echo "$secondary" | cut -d':' -f7|tac -s' '|tr -d ' '))
sec_firstuse=$(printf "%d" $(echo "$secondary" | cut -d':' -f8|tac -s' '|tr -d ' '))
sec_lastuse=$(printf "%d" $(echo "$secondary" | cut -d':' -f9|tac -s' '|tr -d ' '))
sec_guid=$(echo "$secondary" | cut -d':' -f10)
sec_partstart=$(printf "%d" $(echo "$secondary" | cut -d':' -f11|tac -s' '|tr -d ' '))
sec_partmax=$(echo "$secondary" | cut -d':' -f12)
sec_partsize=$(echo "$secondary" | cut -d':' -f13)
sec_pcrc=$(echo "$secondary" | cut -d':' -f14)
}

##################################################################################
##           <partition> Partition Table: GPT-Entries			##
_analyse_partgpt() {
  echo
}

##################################################################################
_get_partinfo () {
##          <partition> information (sysfs)
     # get total sectors - if blank set 0
partsectors=$(grep '[0-9+]$' /sys/block/$dev/$part/size 2>/dev/null || echo 0)
     # get start sector - if blank set 0
partstart=$(grep '[0-9+]$' /sys/block/$dev/$part/start 2>/dev/null || echo 0)
     # set partition size
partend=$(expr \( $partsectors + $partstart \) - 1 )
     # calculate si/iec
partbinary=$(_binary $partsectors $logicalsize)
}

##################################################################################
_get_mount () {
##         <partition> mount information
local work
partfs=' '
partmount=' '
     # is part mounted with the device id?
work=$(grep "^device /dev/${part} mounted" /proc/self/mountstats| cut -d' ' -f5,8)
  if [ x"$work" != "x" ]; then
     partfs=$(echo $work|cut -d' ' -f2)
     partmount="$(echo $work|cut -d' ' -f1)"
     return
  fi
     # is it mounted with the UUID?
work=$(grep "^device /dev/disk/by-uuid/${partuuid} mounted" /proc/self/mountstats| cut -d' ' -f5,8)
  if [ x"$work" != "x" ]; then
     partfs=$(echo $work|cut -d' ' -f2)
     partmount="$(echo $work|cut -d' ' -f1)"
     return
  fi
     # is this the swap?
work=$(grep "/dev/${part}" /proc/swaps | tr '\x09' '\x20' | tr -s ' ' | cut -d' ' -f1,2)
 if [ x"$work" != "x" ]; then
    partfs="swap"
    partmount=$(echo $work | cut -d' ' -f2)
    return
 fi
     # is it mounted with the Label?
work=$(grep "device /dev/disk/by-label/${partlabel} mounted" /proc/self/mountstats | cut -d " " -f5,8)
  if [ x"$work" != "x" ]; then
     partfs=$(echo $work|cut -d' ' -f2)
     partmount="$(echo $work|cut -d' ' -f1)"
     return
  fi
}

##################################################################################
_get_partby () {
##         <partition>  UUID and Label
partuuid=''
partlabel=
partuuid=$(find /dev/disk/by-uuid -lname ../../${part} | cut -d '/' -f5)
partlabel=$(find /dev/disk/by-label -lname ../../${part} | cut -d '/' -f5)
}

##################################################################################
_binary () {
##         subshell for calculating si-/iec-Bytes
local sector=$1 logical=$2 rest iec si i b
iec=$(expr $sector \* $logical)
si=$iec
rest=$(expr $iec \% 1024)
   # calculate size: ICE binary prefixes
i=1
for b in $Bit1024 ; do
   [ $iec -gt 1024 ] && { rest=$(expr $iec \% 1024) ; iec=$(expr $iec / 1024) ;} || break
   i=$(expr $i + 1)
done
rest=$(expr $rest \* 1000 / 1024 )
[ $(expr length $rest) -eq 2 ] && rest="0$rest"
[ $(expr length $rest) -eq 1 ] && rest="00$rest"
 [ $i = 1 ] && rest=B || rest=.$(expr substr $rest 1 1)$b
iec="$iec$rest"

   # calculate size: SI decimal prefixes
if [ $i -gt 1 ]; then
   rest=$(expr $si \% 1000)
   i=1
   for b in $Bit1000 ; do
      [ $si -gt 1000 ] && { rest=$(expr $si \% 1000) ;si=$(expr $si / 1000) ;} || break
      i=$(expr $i + 1)
   done
   [ $(expr length $rest) -eq 2 ] && rest="0$rest"
   [ $(expr length $rest) -eq 1 ] && rest="00$rest"
   [ $i = 1 ] && rest=B || rest=.$(expr substr $rest 1 1)$b
fi
echo "$iec $si$rest"
}

##################################################################################
_printer () {
##        P R I N T I N G
local get="$1" work rc=0 err stars='********************************************************************************************'
[ "x$get" = "x" ] && get=err

case $get in
  header0 ) # print the program-title with Options and advanced information:  if ubuntu-style= release,  operating-system and machine-typ
            printf "\n$PGMNAME - $VERSION  System: %s    Options: $Options\n"  "$DISTRIB_DESCRIPTION $(uname -o) $(uname -m)"
            printf "%-16s %s\n" "$mboard" "$(cat /proc/cmdline 2>/dev/null)"						# the boot-information (cmdline)
         ;;
  header1 ) # print the <device> title with obtained data
            printf "\nDisk: %-11s %s %s  Model: %s  Disk size: %s sectors  (%s/%s)  Logical/physical sector size: %s B/%s B \n" "/dev/${dev}" "($partcnt)" "$parttable" $model $devsectors $(_binary $devsectors $logicalsize) $logicalsize  $physicalsize
         ;;
  header2 ) # print the <partition> title
            printf "%-11s %12s %12s %12s %11s %10s %6s %-20s %-18s %-s\n"  Device  Start End Size "" "" FS Mount Label UUID
         ;;
  liner   ) # print the <partition> information  with obtained data
            printf "/dev/%-6s %12s %12s %12s (%10s/%9s) %6s %-20s %-18s %-s\n"  "${part}:" $partstart $partend $partsize $partsectors $partbinary  "$partfs" "$partmount"  "$partlabel" "$partuuid"
         ;;
  footer0 ) # print the program-footer 	      - possible future use
         ;;
  footer1 ) # print the <partition> footer	      - possible future use
         ;;
  footer2 ) # print the <partition> footer	      - possible future use
         ;;
  gpt     ) _analyse_gpt                          # get and print the <device> gpt-analyse
            printf "\n * Disk: %-11s  Size: %s  P-Table: %s  first/last usable: %s/%s   Entries max./size: %s/%s  GUID: %s\n" "/dev/$dev" $devsectors "$parttable" $prim_firstuse $prim_lastuse $prim_partmax $prim_partsize  $prim_guid
            printf " * %11s  %11s %10s %8s %26s  %12s %12s %12s %12s %8s  %8s\n" ' ' ' ' 'Signature' 'Size'  'Position Current/Backup' '  Entries-Start' 'Header-CRC' 'Part.-CRC' 'Revision' 'Reserve'
            printf " * %11s  %11s %10s %8s  %12s %12s     %12s %12s %12s %12s  %8s\n" ' ' 'primary  :' "$prim_sign" $prim_size $prim_current $prim_backup $prim_partstart $prim_hcrc $prim_pcrc $prim_rev $prim_res
            printf " * %11s  %11s %10s %8s  %12s %12s     %12s %12s %12s %12s  %8s\n" ' ' 'secondary:' "$sec_sign" $sec_size $sec_current $sec_backup $sec_partstart $sec_hcrc $sec_pcrc $sec_rev $sec_res
         # some error-messages
           [ "$prim_sign" != "EFI PART" -o "$sec_sign" != "EFI PART" ] && printf "\t\t\t\t**** Signatur is unequal:  Primary: %9s   Secondary: %9s  ****\n" "$prim_sign" "$sec_sign"
           [ $(expr $devsectors - 1) != $prim_backup ] && printf "\t\t\t\t**** The Backup-Header is not at the end of the hard disk: Backup-LBA:  %d+1  <>  %s Disk size  ****\n" $prim_backup $devsectors
           [ "$prim_current" != "$sec_backup" ] && printf "\t\t\t\t**** Primary-Current unequal to Secondary-Backup:  %s  <>  %s  ****\n" $prim_current $sec_backup
           [ "$prim_backup" != "$sec_current" ] && printf "\t\t\t\t**** Primary-Backup unequal to Secondary-Current: %12s <> %12s ****\n" $prim_backup $sec_current
           [ "$prim_firstuse" != "$sec_firstuse" ] &&printf "\t\t\t\t**** Firt usable LBA for Partitions is unequal Primary: %s  Secondary:  %s  ****\n" $prim_firstuse $sec_firstuse
           [ "$prim_lastuse" != "$sec_lastuse" ] &&printf "\t\t\t\t**** Last usable LBA for Partitions is unequal Primary: %s  Secondary:  %s  ****\n" $prim_lastuse $sec_lastuse
           [ "$prim_guid" != "$sec_guid" ] &&printf "\t\t\t\t**** The Disk-GUID is unequal Primary: %s  Secondary:  %s  ****\n" $prim_guid $sec_guid
           [ "$prim_partmax" != "$sec_partmax" ] &&printf "\t\t\t\t**** max. Partition is unequal Primary: %s  Secondary:  %s  ****\n" $prim_partmax $sec_partmax
           [ "$prim_partsize" != "$sec_partsize" ] &&printf "\t\t\t\t**** Partition-Size is unequal Primary: %s  Secondary:  %s  ****\n" $prim_partsixe $sec_partsize
           [ "$prim_pcrc" != "$sec_pcrc" ] &&printf "\t\t\t\t**** The Partition-CRC32 is unequal Primary: %s  Secondary:  %s  ****\n" $prim_pcrc $sec_pcrc
         # Header for linergpt
           printf "\n *     %-11s  %-8s %-38s %12s %12s   %s \n * \t\t\t       %-38s   %s\t\t%s\n" ' ' ' ' 'GUID' 'Start-LBA' 'End-LBA'  'Attribute values' 'unique GUID' 'Partition name' '(16)  (32)  (48)  (64)'
        ;;
  gpthybrid) # print the <device> mbr-analyse and the gpt-analyse
             _printer gpt
             printf "\n *\t\t\t\t ********************************\n*\t\t\t\t ********** Hybrid-GPT **********\n *\t\t\t\t ********************************\n"
             _printer legacymbr
             printf "\n"
          ;;
  legacymbr) # print the <device> mbr-analyse (not gpt)
             local loader entry1 entry2t4
             loader=$(hexdump -s128 -n2 -ve '2/1 "%02x"'  /dev/$dev)
             entry1=$(hexdump -s 446 -n 16 -v -e '1/1 " %02x " 3/1 " %3u " 1/1 " %02x " 3/1 " %3u " 2/4 " %9u " "\n"' /dev/$dev)
             entry2t4=$(hexdump -s 462 -n 48 -v -e '1/1 " %02x " 3/1 " %3u " 1/1 " %02x " 3/1 " %3u " 2/4 " %9u " "\n"' /dev/$dev)
             printf "\n *\t\t\t\tBoot ID    Start-CHS   ID       End-CHS               LBA1                          LBA2\n"
             printf " *     %-11s  %s  \t%-4s %s  %3s %3s %3s   %s   %3s %3s %3s   %16s              %16s\n"  "/dev/$dev" "$parttable" $loader $entry1
             printf " *\t\t\t\t     %s  %3s %3s %3s   %s   %3s %3s %3s   %16s              %16s\n" $entry2t4
             [ x"$gpt" != "x" ] && printf " *\t\t\t\t**** EFI PART  found in  $gpt         ****\n"
          ;;
  linergpt ) # print the <partition> gpt-analyse
             local start gpt guid lba1 lba2 name attrib
             start=$(expr $prim_partstart \* $logicalsize - $prim_partsize)   # calculate the start of th partition-entries and get them
             gpt=$(hexdump -s$(expr $start + $partno \* $prim_partsize) -n96 -ve '/4 "%08X-" /2 "%04X-" /2 "%04X" "-" 2/1 "%02X" "-" 6/1 "%02X" ":" /4 "%08X-" /2 "%04X-" /2 "%04X" "-" 2/1 "%02X" "-" 6/1 "%02X" ":" 8/1 " %02x " " 0x : " 8/1 " %02x " " 0x : " 4/2 " %04x: " 40/1 "%c" "\n"' /dev/$dev)
             guid=$(echo "$gpt"|cut -d ':' -f1)
             uguid=$(echo "$gpt"|cut -d ':' -f2)
             lba1=$(echo "$gpt" |cut -d ':' -f3 | tac -s ' ' | tr -d '\n ')
             lba2=$(echo "$gpt" |cut -d ':' -f4 | tac -s ' ' | tr -d '\n ')
             attrib=$(echo "$gpt" |cut -d ':' -f5,6,7,8 | tr -d ':')
             name=$(echo "$gpt" |cut -d ':' -f9)
             [ "$guid" = "21686148-6449-6E6F-744E-656564454649" ] && guid="Hah!IdontNeedEFI (BIOS Boot-Partition)"
            printf " *     %-11s  %-8s %-38s %12d %12d %s \n * \t\t\t       %-38s   %s\n" "/dev/$part" "$parttable" "$guid"  $lba1 $lba2  "$attrib" "$uguid" "$name"
         ;;
  linermbr ) # print the <partition> mbr-analyse
             local loader entry1 entry2
             loader=$(hexdump -s128 -n2 -ve '2/1 "%02x"'  /dev/$part)
             entry1=$(hexdump -s 446 -n 16 -v -e '1/1 " %02x " 3/1 " %3u " 1/1 " %02x " 3/1 " %3u " 2/4 " %9u " "\n"' /dev/$part)
             entry2=$(hexdump -s 462 -n 16 -v -e '1/1 " %02x " 3/1 " %3u " 1/1 " %02x " 3/1 " %3u " 2/4 " %9u " "\n"' /dev/$part)
             printf " *     %-11s  %s\t%4s %s  %3s %3s %3s   %s   %3s %3s %3s   %16s              %16s\n"  "/dev/$part" "$parttable" $loader $entry1
             printf " *\t\t\t\t     %s  %3s %3s %3s   %s   %3s %3s %3s   %16s              %16s\n" $entry2
         ;;
  gdisk    ) # print gdisk-information if OptDisk and gpt
             printf "\n------------ gdisk /dev/$dev ------------\n" ; gdisk -l /dev/$dev ; printf "\n"
         ;;
  fdisk    ) # print fdisk-information if OptDisk and not gpt
             printf "\n------------ fdisk -lu /dev/$dev : $partname ------------" ;fdisk -lu /dev/$dev ; printf "\n"
         ;;
  parted   ) # print parted-information if OptParted
             printf "\n------------ parted -s /dev/$dev $PartedPrint print ------------\n" ; parted -s /dev/$dev $PartedPrint print
         ;;
  err      ) # print error without exit
             printf "\t\t$stars\n\t\t* %-88s *\n\t\t$stars\n\n"  "$PGMNAME - Version: $VERSION: >>> Options: $Options  Error: $*" 1>&2
         ;;
    *      ) # print error/unknown select with exit
             printf "\t\t$stars\n\t\t* %-88s *\n\t\t$stars\n\n"  "¿¿ Why ?? $PGMNAME - Version: $VERSION: >>> Options: $Options  Error: $*" 1>&2
             exit 255
         ;;
esac

}

##################################################################################
_control () {
##          C O N T R O L
local work
[ x"$devold" = "x" ]     && { _printer header0 ;}                   # Start and print header0 (Title)
[ "$device" = "@end@" ]  && { _printer footer0; return ;}           # End of the print - no more <device>
[ "$device" = "@pend@" ] && { _printer footer2; return ;}           # End of Partitions - print the cmdline if present   (Boot information)
[ ! -b /dev/${device} ]  && _printer err "wrong <device> : $device" # Is there anything wrong ?
dev=$(echo $device    | sed -n 's/\([a-zA-Z]\+\).*$/\1/p')          # Split: devicename
part=$(echo $device   | grep [0-9]$)                                #    partition - if present
partno=$(echo $part   | sed -n 's/[a-zA-Z]\+\([0-9]\+$\)/\1/p')     #        partition number
# Device changed?
if [ "$dev" != "$devold" ] ; then
   _get_devinfo                    # Get sysfs information for the device: logicalsize ...
   [ $OptRoot != 0 ]    && { _analyse_mbr ;}       # root: Name of the partition table
   [ $OptList != 0 ]    && { _printer header1 ;}   # Device-Title and device information
   [ $OptAnalyse != 0 ] && { _printer $parttyp ;}  #    is there an GPT?
   [ $OptList != 0 ]    && { _printer header2 ;}   # Partition-Title
   devold=$dev   # store device for the further loop
   # OptList and OptAnalyse: Loop for the partitions
   [ $OptList != 0 -o $OptAnalyse != 0 ]    && _LouPin "$(sed -ne 's/^.*\('${dev}'[0-9]\+$\)/\1/p' /proc/partitions) @pend@"
   # parted?
   [ $OptParted != 0 ]  &&  { $(echo $parted | grep -q "parted") && _printer parted ;}
   # fdisk/gdisk?
   [ $OptDisk != 0 ]    && { [ "$partname" = "gpt" ] && _printer gdisk || _printer fdisk ;}
fi
# Partition changed?
if [ "$part" != "$partold" ] ; then
   partold=$part   # store device for the further loop
   # Get information: UUID, Label, Mount, Size ...
   [ $OptList  != 0 ] && { _get_partby; _get_mount ;_get_partinfo ;}
   # We are root?
   if [ $OptRoot != 0 ] ; then
       # If no gpt, then look for then part-id
       [ x"$work" = "x" ] && work=$(hexdump -s$((434 + $(($partno*16)))) -n1 -e '1/1 "%02x"' /dev/$dev 2>/dev/null)
       # set the information, if id=05 - we are sure
       [ "$work" = "05" ] && { partfs="ID=$work": partmount=extended ;}
   fi
       [ $OptList  != 0 ] && { _printer liner ;}
       if [  $OptAnalyse != 0 ] ;then   # OptAnalyse?
           # Which partition tabel?
           [ "$parttyp" = "gpt" -o "$parttyp" = "gpthybrid" ]   && { _printer linergpt ;} || { _printer linermbr ;}
       fi
fi
}

##################################################################################
##			LOOP for devices and/or partitions			##
##################################################################################
_LouPin () {
local dsn="$@"
    for device in $dsn
      do   _control
    done
}

##################################################################################
#      S T A R T   R U N N I N G                                                ##
        _LouPin "$DevDSN @end@"
##################################################################################

exit