///|
fn help_message() -> String {
let message =
#|Usage: hexdump [OPTION]... [FILE]...
#|Display concatenated files as numbers and characters; no FILE reads stdin.
#| -b, --one-byte-octal one-byte octal
#| -c, --one-byte-char escaped characters
#| -C, --canonical hexadecimal bytes with ASCII
#| -d, --two-bytes-decimal two-byte unsigned decimal
#| -o, --two-bytes-octal two-byte octal
#| -x, --two-bytes-hex two-byte hexadecimal
#| -X, --one-byte-hex one-byte hexadecimal
#| -n, --length=N display at most N input bytes
#| -s, --skip=N skip N input bytes in named files
#| -v, --no-squeezing display duplicate blocks
#| -h, --help display this help and exit
#| -V, --version display version and exit
message
}
///|
async fn main {
let metadata_args = @env.args()[1:]
if metadata_args.length() == 1 && metadata_args[0] == "--version" {
@stdio.stdout.write("hexdump 0.1.1\n")
return
}
let parsed = @cli.parse(@env.args()[1:], [
@cli.flag("one-byte-octal", short='b', repeatable=true),
@cli.flag("one-byte-char", short='c', repeatable=true),
@cli.flag("canonical", short='C', repeatable=true),
@cli.flag("two-bytes-decimal", short='d', repeatable=true),
@cli.flag("two-bytes-octal", short='o', repeatable=true),
@cli.flag("two-bytes-hex", short='x', repeatable=true),
@cli.flag("one-byte-hex", short='X', repeatable=true),
@cli.option("length", short='n', repeatable=true),
@cli.option("skip", short='s', repeatable=true),
@cli.flag("no-squeezing", short='v', repeatable=true),
@cli.flag("help", short='h'),
@cli.flag("version", short='V'),
]) catch {
@cli.CliError(option~, message~, ..) => {
@stdio.stderr.write("hexdump: \{message}: '\{option}'\n")
@sys.exit(1)
return
}
}
if parsed.contains("help") {
@stdio.stdout.write(help_message() + "\n")
return
}
if parsed.contains("version") {
@stdio.stdout.write("hexdump \{command_version}\n")
return
}
let (skip, remaining) = try {
let skip = parse_count(parsed.last_value("skip").unwrap_or("0"))
let remaining = match parsed.last_value("length") {
Some(n) => Some(parse_count(n))
None => None
}
(skip, remaining)
} catch {
HexdumpError(message) => {
@stdio.stderr.write("hexdump: \{message}\n")
@sys.exit(1)
return
}
}
let modes : Array[String] = []
if remaining == Some(0L) {
return
}
for name in parsed.order {
if name == "one-byte-octal" ||
name == "one-byte-char" ||
name == "canonical" ||
name == "two-bytes-decimal" ||
name == "two-bytes-octal" ||
name == "two-bytes-hex" ||
name == "one-byte-hex" {
modes.push(name)
}
}
if modes.is_empty() {
modes.push("default")
}
let state : DumpState = {
modes,
verbose: parsed.contains("no-squeezing"),
pending: [],
skip,
remaining,
offset: 0L,
previous: None,
squeezed: false,
}
let sources = if parsed.operands.is_empty() { ["-"] } else { parsed.operands }
let mut failed = false
for path in sources {
if state.skip == 0L && state.remaining == Some(0L) {
break
}
try {
if path == "-" {
if state.skip > 0L {
@stdio.stderr.write(
"hexdump: skipping stdin requires a public seekable-stream API\n",
)
failed = true
break
} else {
state.read(@stdio.stdin)
}
} else {
let file = @fs.open(path, mode=ReadOnly)
defer file.close()
state.read(file)
}
} catch {
err => {
@stdio.stderr.write("hexdump: \{path}: \{err}\n")
failed = true
}
}
}
if !state.pending.is_empty() {
@stdio.stdout.write(state.row())
}
if state.offset > 0L {
@stdio.stdout.write(
number(
state.offset,
16,
if modes.last() == Some("canonical") {
8
} else {
7
},
) +
"\n",
)
}
if failed {
@sys.exit(1)
}
}