[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#684128: PATCH: choice of binary or decimal disk storage units is runtime-configurable



On Thu, 9 Aug 2012 10:59:20 -0400
Joey Hess <joeyh@debian.org> wrote:

> I hate to bring this news, but this cannot be used in the installer,
> because shell arrays are a bashism, and the installer uses busybox sh.

Thanks for pointing that out. It seems that shell arrays are more of a
ksh-ism; see the manual page for mksh(1). I did try to avoid obvious
bashisms, but I didn't know that Bourne shell doesn't have arrays, or
that its arithmetic was so crippled.

Fixed now; I've tested this version with busybox, and it seems to work
fine. See attachments.

checksums:
425334e865579c218e15f66fa018dd50  partman-base_158.diff
0e38d0168a14c42cccd24857c0fd219c  cvt.sh

> FWIW, it is possible, though painful to emulate shell arrays using
> eval, or other tricks.

I'll remember that trick; it will be useful sometime. In this case, the
arrays are read-only, so a case statement wrapped up in a function will
do fine. Arrays are just functions on a subset of integers, anyway.

As a special bonus, this version outputs "KiB", "MiB", etc, when
operating in binary mode.

As an extra special bonus, it now has support for {peta,pebi}bytes, just
in case somebody wants to set up an array of 400 three-terabyte disks. 
Unfortunately, this isn't currently working, apparently because of
64-bit arithmetic overflows in "expr". (Isn't that what "expr" is
supposed to avoid?)

For those who don't care, a decimal petabyte is only 8/9 of a binary
pebibyte.


-- Ian Bruce
--- partman-base-158/lib/base.sh.orig	2012-01-03 07:49:51.000000000 -0800
+++ partman-base-158/lib/base.sh	2012-08-10 00:55:43.000000000 -0700
@@ -278,110 +278,142 @@
 	else
 		return 1
 	fi
 }
 
+name_units ()
+{
+	local n=$1 s
+	if [ "$USE_BINARY_UNITS" ]
+	then
+		case $n in
+		0) s=B ;;
+		1) s=KiB ;;
+		2) s=MiB ;;
+		3) s=GiB ;;
+		4) s=TiB ;;
+		5) s=PiB ;;
+		esac
+	else
+		case $n in
+		0) s=B ;;
+		1) s=kB ;;
+		2) s=MB ;;
+		3) s=GB ;;
+		4) s=TB ;;
+		5) s=PB ;;
+		esac
+	fi
+	echo $s
+}
+
+disk_units ()
+{
+	local n=$1 r
+	if [ "$USE_BINARY_UNITS" ]
+	then
+		case $n in   # (2^10)^{0,1,2,3,4,5}
+		0) r=1 ;;
+		1) r=1024 ;;
+		2) r=1048576 ;;
+		3) r=1073741824 ;;
+		4) r=1099511627776 ;;
+		5) r=1125899906842624 ;;
+		esac
+	else
+		case $n in   # (10^3)^{0,1,2,3,4,5}
+		0) r=1 ;;
+		1) r=1000 ;;
+		2) r=1000000 ;;
+		3) r=1000000000 ;;
+		4) r=1000000000000 ;;
+		5) r=1000000000000000 ;;
+		esac
+	fi
+	echo $r
+}
+
 longint2human () {
-	local longint suffix bytes int frac deci
+	local longint radix bytes int frac deci exp
 	# fallback value for $deci:
 	deci="${deci:-.}"
-	case ${#1} in
-	    1|2|3)
-		suffix=B
-		longint=${1}00
-		;;
-	    4|5|6)
-		suffix=kB
-		longint=${1%?}
-		;;
-	    7|8|9)
-		suffix=MB
-		longint=${1%????}
-		;;
-	    10|11|12)
-		suffix=GB
-		longint=${1%???????}
-		;;
-	    *)
-		suffix=TB
-		longint=${1%??????????}
-		;;
-	esac
-	longint=$(($longint + 5))
-	longint=${longint%?}
-	int=${longint%?}
-	frac=${longint#$int}
-	printf "%i%s%i %s\n" $int $deci $frac $suffix
+	bytes=$1
+	exp=6   # possible units
+	while [ $exp -gt 0 ]
+	do
+		exp=$((exp-1))
+		radix=$(disk_units $exp)
+#		expr $bytes ">=" $radix >/dev/null && break
+		expr 1000 \* $bytes ">=" 995 \* $radix >/dev/null && break
+	done
+	longint=$(expr $bytes \* 1000 + $radix \* 5)
+	int=$(expr $longint / \( $radix \* 1000 \) )
+	frac=$(expr $longint % \( $radix \* 1000 \) / \( $radix \* 10 \) )
+	if [ $exp -gt 0 ]
+	then
+		printf "%i%s%02i %s\n" $int $deci $frac $(name_units $exp)
+	else
+		printf "%i %s\n" $int $(name_units $exp)
+	fi
 }
 
 human2longint () {
-	local human orighuman gotb suffix int frac longint
+	local human orighuman gotb suffix int frac longint exp
 	set -- $*; human="$1$2$3$4$5" # without the spaces
 	orighuman="$human"
 	human=${human%b} #remove last b
 	human=${human%B} #remove last B
 	gotb=''
 	if [ "$human" != "$orighuman" ]; then
 		gotb=1
 	fi
 	suffix=${human#${human%?}} # the last symbol of $human
 	case $suffix in
-	k|K|m|M|g|G|t|T)
+	k|K|m|M|g|G|t|T|p|P)
 		human=${human%$suffix}
 		;;
 	*)
 		if [ "$gotb" ]; then
 			suffix=B
 		else
-			suffix=''
+			suffix=M # default to megabytes
 		fi
 		;;
 	esac
 	int="${human%[.,]*}"
 	[ "$int" ] || int=0
 	frac=${human#$int}
 	frac="${frac#[.,]}0000" # to be sure there are at least 4 digits
 	frac=${frac%${frac#????}} # only the first 4 digits of $frac
-	longint=$(expr "$int" \* 10000 + "$frac")
+	longint=$(expr $int \* 10000 + $frac)
 	case $suffix in
 	b|B)
-		longint=${longint%????}
-		[ "$longint" ] || longint=0
-		;;
+		exp=0 ;;
 	k|K)
-		longint=${longint%?}
-		;;
+		exp=1 ;;
 	m|M)
-		longint=${longint}00
-		;;
+		exp=2 ;;
 	g|G)
-		longint=${longint}00000
-		;;
+		exp=3 ;;
 	t|T)
-		longint=${longint}00000000
-		;;
-	*) # no suffix:
-		# bytes
-		#longint=${longint%????}
-		#[ "$longint" ] || longint=0
-		# megabytes
-		longint=${longint}00
-		;;
+		exp=4 ;;
+	p|P)
+		exp=5 ;;
 	esac
-	echo $longint
+	expr $longint \* $(disk_units $exp) / 10000
 }
 
 valid_human () {
 	local IFS patterns
 	patterns='[0-9][0-9]* *$
 [0-9][0-9]* *[bB] *$
-[0-9][0-9]* *[kKmMgGtT] *$
-[0-9][0-9]* *[kKmMgGtT][bB] *$
+[0-9][0-9]* *[kKmMgGtTpP] *$
+[0-9][0-9]* *[kKmMgGtTpP][bB] *$
 [0-9]*[.,][0-9]* *$
 [0-9]*[.,][0-9]* *[bB] *$
-[0-9]*[.,][0-9]* *[kKmMgGtT] *$
-[0-9]*[.,][0-9]* *[kKmMgGtT][bB] *$'
+[0-9]*[.,][0-9]* *[kKmMgGtTpP] *$
+[0-9]*[.,][0-9]* *[kKmMgGtTpP][bB] *$'
 	IFS="$NL"
 	for regex in $patterns; do
 		if expr "$1" : "$regex" >/dev/null; then return 0; fi
 	done
 	return 1

Attachment: cvt.sh
Description: application/shellscript


Reply to: