///|
priv suberror CliError {
CliError(Int32, String)
}
///|
priv enum Style {
Normal
Plain
Include
Bits
Little
}
///|
priv struct Seek {
relative : Bool
negative : Bool
amount : Int64
}
///|
priv struct Options {
style : Style
cols : Int32
group : Int32
upper : Bool
ebcdic : Bool
autoskip : Bool
reverse : Bool
limit : Int64?
seek : Seek?
display_offset : UInt64
name : String?
}
///|
fn hex_val(byte : Byte) -> Int32 {
match byte {
b'0'..<=b'9' => byte.to_int() - 0x30
b'a'..<=b'f' => byte.to_int() - 0x61 + 10
b'A'..<=b'F' => byte.to_int() - 0x41 + 10
_ => -1
}
}
///|
fn magnitude(text : String) -> (Bool, UInt64) {
let mut index = 0
while index < text.length() &&
text[index] is (' ' | '\t' | '\n' | '\r' | '\u000b' | '\u000c') {
index += 1
}
let negative = index < text.length() && text[index] == '-'
if index < text.length() && text[index] is ('-' | '+') {
index += 1
}
let mut radix = 10
if index < text.length() && text[index] == '0' {
radix = 8
if index + 2 < text.length() &&
text[index + 1] is ('x' | 'X') &&
text[index + 2] is ('0'..<='9' | 'a'..<='f' | 'A'..<='F') {
radix = 16
index += 2
}
}
let mut value = 0UL
let mut overflow = false
while index < text.length() {
let digit = match text[index] {
'0'..<='9' => text[index].to_int() - 0x30
'a'..<='f' => text[index].to_int() - 0x61 + 10
'A'..<='F' => text[index].to_int() - 0x41 + 10
_ => break
}
if digit >= radix {
break
}
if value > (18446744073709551615UL - digit.to_uint64()) / radix.to_uint64() {
overflow = true
} else if !overflow {
value = value * radix.to_uint64() + digit.to_uint64()
}
index += 1
}
(negative, if overflow { 18446744073709551615UL } else { value })
}
///|
fn number(text : String) -> Int64 {
let (negative, value) = magnitude(text)
if negative {
if value >= 9223372036854775808UL {
-9223372036854775807L - 1L
} else {
-value.reinterpret_as_int64()
}
} else {
(if value > 9223372036854775807UL { 9223372036854775807UL } else { value }).reinterpret_as_int64()
}
}
///|
fn parse_seek(text : String) -> Seek {
let relative = text.has_prefix("+")
let start = if relative { 1 } else { 0 }
let negative = start < text.length() && text[start] == '-'
let start = start + (if negative { 1 } else { 0 })
{ relative, negative, amount: number(text[start:].to_owned()), }
}
///|
fn checked_add(
left : Int64,
right : Int64,
status : Int32,
message : String,
) -> Int64 raise CliError {
if (right > 0L && left > 9223372036854775807L - right) ||
(right < 0L && left < -9223372036854775807L - 1L - right) {
raise CliError(status, message)
}
left + right
}
///|
fn normalized_args(args : ArrayView[String]) -> Array[String] {
args.map(arg => {
match arg {
"-ps" | "-postscript" | "--postscript" | "-plain" => "--plain"
"--revert" | "-revert" => "--reverse"
"-autoskip" => "--autoskip"
"-bits" => "--bits"
"-littleendian" => "--little"
"-ebcdic" => "--ebcdic"
"-groupsize" | "-group" => "--group"
"-offset" => "--offset"
"-cols" => "--cols"
"-skip" | "-seek" | "--skip" => "--seek"
"-len" => "--len"
"-name" => "--name"
"-upper" => "--upper"
"-include" => "--include"
_ => arg
}
})
}
///|
fn options(parsed : @cli.ParsedArgs) -> Options raise CliError {
let mut style = Normal
let mut autoskip = false
for name in parsed.order {
match name {
"plain" => style = Plain
"include" => style = Include
"bits" => style = Bits
"little" => style = Little
"autoskip" => autoskip = !autoskip
_ => ()
}
}
let default_cols = match style {
Plain => 30
Include => 12
Bits => 6
_ => 16
}
let cols = number(
parsed.last_value("cols").unwrap_or(default_cols.to_string()),
).to_int()
let cols = if cols == 0 && !(style is Plain) { default_cols } else { cols }
if cols < 0 ||
(cols < 1 && !(style is Plain)) ||
((style is Normal || style is Bits || style is Little) && cols > 256) {
raise CliError(1, "xxd: invalid number of columns (max. 256).")
}
let default_group = match style {
Bits => 1
Normal => 2
Little => 4
_ => 0
}
let group = number(
parsed.last_value("group").unwrap_or(default_group.to_string()),
).to_int()
let group = if group < 0 { default_group } else { group }
let group = if group < 1 || group > cols {
cols
} else if style is Little && (group & (group - 1)) != 0 {
raise CliError(
1, "xxd: number of octets per group must be a power of 2 with -e.",
)
} else {
group
}
let seek = parsed.last_value("seek").map(parse_seek)
let (negative, value) = magnitude(parsed.last_value("offset").unwrap_or("0"))
{
style,
cols,
group,
upper: parsed.contains("upper"),
ebcdic: parsed.contains("ebcdic"),
autoskip,
reverse: parsed.contains("reverse"),
seek,
limit: parsed.last_value("len").map(number).filter(n => n >= 0L),
display_offset: if negative {
0UL - value
} else {
value
},
name: parsed.last_value("name"),
}
}