#!/bin/bash
# Author: Steven Shiau <steven _at_ nchc org tw>
# License: GPL

#
export LC_ALL=C

#
prog="$(basename $0)"
USAGE() {
   echo "Usage: $prog [OPTION] PARTITION VALUE1 VALUE2..."
   echo "PARTITION is the partition device name. e.g. /dev/sda1"
   echo "VALUE is one of these value: start, end, size, type, filesystem or flags"
   echo "OPTION:"
   echo "-u, --unit UNIT     When the VALUE1, VALUE2... is about size, show the size with unit UNIT (s, kB, MB, GB... which parted accepts)."
   echo "Ex:" 
   echo "To list the /dev/sda1 size with unit sector"
   echo "  $prog -u s /dev/sda1 size"
}

#
check_col_idx() {
  # DEV is like /dev/hda
  # wanted is like "start", "end", "file" (since it's "file system" in SuSE 10.0, and filesystem in Redhat/FC)
  # Example in FC5 with parted-1.7.1-15.fc5
  #[root@fc5101 tmp]# LC_ALL=C parted -s /dev/hda p
  #
  #Disk /dev/hda: 4295MB
  #Sector size (logical/physical): 512B/512B
  #Partition Table: msdos
  #
  #Number  Start   End     Size    Type     File system  Flags
  # 1      32.3kB  4285MB  4285MB  primary  ntfs         boot
  #
  #For Mac OS GPT:
  #Model: ATA Hitachi HTS54168 (scsi)
  #Disk /dev/sda: 80.0GB
  #Sector size (logical/physical): 512B/512B
  #Partition Table: gpt
  #
  #Number  Start   End     Size    File system  Name                  Flags
  # 1      20.5kB  210MB   210MB   fat32        EFI System Partition  boot
  # 2      210MB   38.3GB  38.1GB  hfs+         Untitled
  # 3      38.4GB  41.6GB  3221MB  fat32        Untitled
  #
  # Different version of parted has different output format, that's why it's so complicated here.
  local DEV=$1
  local wanted=$2
  part_info_tmp=`mktemp /tmp/part_tmp.XXXXXX`
  LC_ALL=C parted -s $DEV print > $part_info_tmp 2>/dev/null
  # force to change the output temp file, no space in "File system"
  LC_ALL=C perl -pi -e "s/File system/Filesystem/g" $part_info_tmp
  # The seq is put in the order of possibility in clonezilla, i.e. filesystem is most used one, then size.
  for i in 6 4 5 3 2 1 7; do
    col="$(grep -i flags $part_info_tmp | awk -F" " "{print \$$i}")"
    if [ -n "$(echo $col | grep -i $wanted)" ]; then
      col_idx=$i
      break
    fi
  done
  [ -f "$part_info_tmp" ] && rm -f $part_info_tmp
  echo $col_idx
} # end of check_col_idx

#
get_from_parted() {
  p="$(basename $part)"
  target_hd="/dev/${p:0:3}"
  part_index="${p:3}"
  
  part_info=`mktemp /tmp/part.XXXXXX`
  [ -n "$unit" ] && unit_opt="unit $unit"
  LC_ALL=C parted -s $target_hd $unit_opt print > $part_info 2>/dev/null
  
  case "$wanted" in
    start)
       start_idx="$(check_col_idx $target_hd start)"
       [ -z "$start_idx" ] && return 1
       part_start=$(grep -E "^[[:space:]]*$part_index\>" $part_info | awk -F" " "{print \$$start_idx}")
       echo "$part_start"
       ;;
    end)
       end_idx="$(check_col_idx $target_hd end)"
       [ -z "$end_idx" ] && return 1
       part_end=$(grep -E "^[[:space:]]*$part_index\>" $part_info | awk -F" " "{print \$$end_idx}")
       echo "$part_end"
       ;;
    size)
       size_idx="$(check_col_idx $target_hd size)"
       [ -z "$size_idx" ] && return 1
       part_size=$(grep -E "^[[:space:]]*$part_index\>" $part_info | awk -F" " "{print \$$size_idx}")
       echo "$part_size"
       ;;
    type)
       type_idx="$(check_col_idx $target_hd type)"
       [ -z "$type_idx" ] && return 1
       part_type=$(grep -E "^[[:space:]]*$part_index\>" $part_info | awk -F" " "{print \$$type_idx}")
       echo "$part_type"
       ;;
    filesystem|fs)
       filesystem_idx="$(check_col_idx $target_hd file)"
       [ -z "$filesystem_idx" ] && return 1
       part_fs=$(grep -E "^[[:space:]]*$part_index\>" $part_info | awk -F" " "{print \$$filesystem_idx}")
       echo "$part_fs"
       ;;
    flags)
       flags_idx="$(check_col_idx $target_hd flags)"
       [ -z "$flags_idx" ] && return 1
       part_flags=$(grep -E "^[[:space:]]*$part_index\>" $part_info | awk -F" " "{print \$$flags_idx}")
       echo "$part_flags"
       ;;
  esac
  
  [ -f "$part_info" ] && rm -f $part_info
} # end of get_from_parted

#
get_fs_from_blkid() {
  local part_="$1" 
  local blkinfo TYPE
  if type blkid &>/dev/null; then
    blkinfo="$(mktemp /tmp/blkinfo.XXXXXX)"
    # blikd output example:
    # /dev/sda1: LABEL="/boot" UUID="a9cb47b6-e3ff-4a26-80e2-3fa8d30702e8" SEC_TYPE="ext2" TYPE="ext3" 
    # /dev/VolGroup00/LogVol00: UUID="110bc035-6920-4732-b117-5124843b93b9" SEC_TYPE="ext2" TYPE="ext3" 
    # /dev/VolGroup00/LogVol01: TYPE="swap" UUID="af85b756-39fa-48f0-80e7-b2128d5c87ab"
    # We want to filter to filter them as: TYPE="ext3" only
    LC_ALL=C blkid -c /dev/null $part_ | grep -o -E '\<TYPE="[^[:space:]]*"($|[[:space:]]+)' > $blkinfo
    TYPE=""
    . $blkinfo
    # If the result is vfat, it's too fuzzy, since there are fat12/fat16/fat32, and partimage does not support fat12.
    if [ -n "$(echo $TYPE | grep -iE "vfat")" ]; then
      if [ "$(file -Ls $part_ | grep -iw "fat" | grep -iw "12 bit")" ]; then
        # Ex. for FAT12:
	# /dev/sda1: x86 boot sector, mkdosfs boot message display, code offset 0x3c, OEM-ID " mkdosfs", sectors/cluster 16, root entries 512, sectors 63252 (volumes <=32 MB) , Media descriptor 0xf8, sectors/FAT 12, heads 255, serial number 0x487771fc, label: "           ", FAT (12 bit)
       TYPE="fat12"
      fi
    fi
    echo "$TYPE"
    [ -f "$blkinfo" ] && rm -f $blkinfo
  fi
} # end of get_fs_from_blkid
#
get_fs_from_partclone_fstype() {
  local part_="$1" 
  local fstypeinfo TYPE
  if type partclone.fstype &>/dev/null; then
    fstypeinfo="$(mktemp /tmp/fstypeinfo.XXXXXX)"
    # partclone.fstype output example:
    # partclone.fstype /dev/hda3 2>/dev/null
    # TYPE="vmfs"
    # We want to filter to filter them as: TYPE="vmfs" only
    LC_ALL=C partclone.fstype $part_ 2>/dev/null| grep -o -E '\<TYPE="[^[:space:]]*"($|[[:space:]]+)' > $fstypeinfo
    TYPE=""
    . $fstypeinfo
    echo "$TYPE"
    [ -f "$fstypeinfo" ] && rm -f $fstypeinfo
  fi
} # end of get_fs_from_partclone_fstype

# Some notes:
# First priority is to use blkid to get filesystem, 2 reasons:
# (1) Since parted seems to have problem with ntfs in some case:
# LC_ALL=C parted -s /dev/hdb p
# Model: VMware Virtual IDE Hard Drive (ide)
# Disk /dev/hdb: 8590MB
# Sector size (logical/physical): 512B/512B
# Partition Table: msdos
# 
# Number  Start   End     Size    Type     File system  Flags
#  1      32.3kB  8579MB  8579MB  primary  ntfs         boot
# 
# [root@drblrhvmnet101 ~]# LC_ALL=C parted -s /dev/hdb1 p
# Error: Can't have a partition outside the disk!
#
# (2) Moreover, the method in get_from_parted does NOT work for LV in LVM... blkid can solve the problem. The weakness is blkid does not provide size info, but it's not so used very often in clonezilla, and normally size is not important when cloning.

#
##############
#### MAIN ####
##############
#
while [ $# -gt 0 ]; do
  case "$1" in
    -u|--unit)
            shift
            if [ -z "$(echo $1 |grep ^-.)" ]; then
              # skip the -xx option, in case 
	      unit="$1"
              shift
            fi
            [ -z "$unit" ] && USAGE >&2 && exit 1
	    ;;
    -*)     echo "${0}: ${1}: invalid option" >&2
            USAGE >& 2
            exit 2 ;;
    *)      break ;;
  esac
done
# part is like /dev/hda1
# request is one of start, end, size, type, filesystem or flags.
part="$1"
shift
request="$*"

[ -z "$part" -o -z "$request" ] && exit 1

#
for wanted in $request; do
  case "$wanted" in
    filesystem|fs)
       fs="$(get_fs_from_blkid $part)"
       if [ -z "$fs" ]; then
	 fs="$(get_fs_from_partclone_fstype $part)"
       fi
       if [ -z "$fs" ]; then
         get_from_parted
       else
         echo "$fs"
       fi
       ;;
    *)
       get_from_parted
       ;;
  esac
done
