Add negative decimal input, regex for prefixes, bits per int detection

This commit is contained in:
2024-02-01 09:34:49 +01:00
parent f1fe945ecd
commit b0f1f53865

View File

@@ -14,6 +14,31 @@
CMDNAME=${0##*/} # script name CMDNAME=${0##*/} # script name
# some default values (blocks separator padchar)
# Attention: For output base 10, obase is 1
declare -i ibase=0 obase=0 padding=0 prefix=1 ogroup=0 intbits
# find out int size (bits) - suppose 2-complement, and 8 bits char
printf -v _b "%x" -1
(( intbits = ${#_b} * 4 ))
declare -rA _bases=( # -f/-b accepted values
[2]=2 [b]=2 [B]=2
[8]=8 [o]=8 [O]=8
[10]=10 [d]=10 [D]=10
[16]=16 [h]=16 [H]=16
[a]=-1 [g]=-1
)
declare -A _pad=( # group separator
[2]=" " [8]=" " [10]="," [16]=" "
)
declare -rA _ogroup=( # group size
[2]=8 [8]=3 [10]=3 [16]=4
)
declare -rA _oprefix=( # output prefix
[2]="2#" [8]="0" [10]="" [16]="0x"
)
usage() { usage() {
printf "usage: %s [OPTIONS] [NUMBER]...\n" "$CMDNAME" printf "usage: %s [OPTIONS] [NUMBER]...\n" "$CMDNAME"
printf "Use '%s -h' for more help\n" "$CMDNAME" printf "Use '%s -h' for more help\n" "$CMDNAME"
@@ -29,6 +54,7 @@ usage: $CMDNAME [OPTIONS] [NUMBER]...
-p, --padding 0-pad output on block boundary (implies -g) -p, --padding 0-pad output on block boundary (implies -g)
-n, --noprefix remove base prefixes in output -n, --noprefix remove base prefixes in output
-h, --help this help -h, --help this help
-- end of options
$CMDNAME output the NUMBERS arguments in different bases. If no NUMBER is $CMDNAME output the NUMBERS arguments in different bases. If no NUMBER is
given, standard input will be used. given, standard input will be used.
@@ -45,9 +71,12 @@ INPUT NUMBER
If input base is not specified, some prefixes are supported. If input base is not specified, some prefixes are supported.
'b' or '2/' for binary, '0', 'o' or '8/' for octal, '0x', 'x' or 'b' or '2/' for binary, '0', 'o' or '8/' for octal, '0x', 'x' or
'16/' for hexadecimal, and 'd' or '10/' for decimal. '16/' for hexadecimal, and 'd' or '10/' for decimal.
If no prefix, decimal is assumed. If no above prefix is found, decimal is assumed.
Decimal input may be signed or unsigned, with limits imposed by current
Bash (here: $intbits bits).
OUTPUT OUTPUT
Decimal output is always unsigned.
By default, the input number is shown converted in the 4 supported By default, the input number is shown converted in the 4 supported
bases (16, 10, 8, 2, in this order), separated by one tab character. bases (16, 10, 8, 2, in this order), separated by one tab character.
Without '-n' option, all output numbers but decimal will be prefixed: Without '-n' option, all output numbers but decimal will be prefixed:
@@ -66,12 +95,12 @@ EXAMPLES
$ $CMDNAME 0 $ $CMDNAME 0
0x0 0 0 2#0 0x0 0 0 2#0
$ $CMDNAME -n 2/100
4 4 4 100
$ $CMDNAME 123456 $ $CMDNAME 123456
0x1e240 123456 0361100 2#11110001001000000 0x1e240 123456 0361100 2#11110001001000000
$ $CMDNAME -n 2/100
4 4 4 100
$ $CMDNAME -n 0x1e240 $ $CMDNAME -n 0x1e240
1e240 123456 361100 11110001001000000 1e240 123456 361100 11110001001000000
@@ -79,6 +108,10 @@ EXAMPLES
$ $CMDNAME -bng 0x1e240 $ $CMDNAME -bng 0x1e240
1 11100010 01000000 1 11100010 01000000
Negative input (decimal only):
$ $CMDNAME -x -- -1
0xffffffffffffffff
Input base indication, left padding binary output, no prefix: Input base indication, left padding binary output, no prefix:
$ $CMDNAME -nbp -f8 361100 $ $CMDNAME -nbp -f8 361100
00000001 11100010 01000000 00000001 11100010 01000000
@@ -91,30 +124,13 @@ EXAMPLES
Long options, with separator and padding: Long options, with separator and padding:
$ $CMDNAME --to=16 --noprefix --padding --group=: 12345 $ $CMDNAME --to=16 --noprefix --padding --group=: 12345
0001:e240 0001:e240
TODO
Add option for signed/unsigned integer output.
Remove useless octal output ?
_EOF _EOF
} }
# some default values (blocks separator padchar)
# Attention: For output base 10, obase is 1
declare -i ibase=0 obase=0 padding=0 prefix=1 ogroup=0
declare -rA _bases=(
[2]=2 [b]=2 [B]=2
[8]=8 [o]=8 [O]=8 [0]=8
[10]=10 [d]=10 [D]=10
[16]=16 [h]=16 [H]=16 [0x]=16
[a]=-1 [g]=-1
)
declare -A _pad=(
[2]=" " [8]=" " [10]="," [16]=" "
)
declare -rA _ogroup=(
[2]=8 [8]=3 [10]=3 [16]=4
)
declare -rA _oprefix=(
[2]="2#" [8]="0" [10]="" [16]="0x"
)
zero_pad() { zero_pad() {
local n="$1" str="$2" local n="$1" str="$2"
@@ -142,11 +158,16 @@ split() {
} }
bin() { bin() {
local n bits="" local str=""
for (( n = $1 ; n > 0 ; n >>= 1 )); do local -i n dec="$1"
bits=$((n&1))$bits
# take care of negative numbers, as >> operator keeps the sign.
# 'intbits' is size of integer in bits in current shell.
for (( n = 0 ; dec && (n < intbits); n++ )); do
str="$(( dec & 1 ))$str"
(( dec >>= 1 ))
done done
printf "%s\n" "${bits:-0}" printf "%s\n" "${str:-0}"
} }
hex() { hex() {
@@ -213,8 +234,8 @@ parse_opts() {
esac esac
shift shift
done done
# parse remaining arguments # next are numbers to convert, if any
if (($# > 0)); then # type if (($# > 0)); then
args=("$@") args=("$@")
fi fi
} }
@@ -230,23 +251,20 @@ addprefix() {
} }
stripprefix() { stripprefix() {
local number="$1" [[ $1 =~ ^(0x|b|o|d|x|.*/) ]]
number=${number#0x} printf "%s" "${1#"${BASH_REMATCH[1]}"}"
number=${number#[bodx]}
number=${number#*/}
printf "%s" "$number"
} }
guessbase() { guessbase() {
local input="$1" local input="$1"
local -i base=0 local -i base=0
if [[ $input =~ ^b || $input =~ ^2/ ]]; then if [[ $input =~ ^(b|2/) ]]; then
base=2 base=2
elif [[ $input =~ ^0x || $input =~ ^x || $input =~ ^16/ ]]; then elif [[ $input =~ ^(0x|x|16/) ]]; then
base=16 base=16
elif [[ $input =~ ^0 || $input =~ ^o || $input =~ ^8/ ]]; then elif [[ $input =~ ^(0|o|8/) ]]; then
base=8 base=8
elif [[ $input =~ ^d || $input =~ ^10/ ]]; then elif [[ $input =~ ^(d|10/) ]]; then
base=10 base=10
fi fi
return $(( base ? base : 10 )) return $(( base ? base : 10 ))
@@ -261,7 +279,9 @@ doit() {
fi fi
inum=$(stripprefix "$number") inum=$(stripprefix "$number")
(( decval = "$base#$inum" )) # input value in decimal # convert input value to decimal
(( base == 10 )) && (( decval = inum ))
(( base != 10 )) && (( decval = "$base#$inum" ))
# mask for desired output: 1=decimal, others are same as base # mask for desired output: 1=decimal, others are same as base
if (( ! _obase )); then if (( ! _obase )); then