The Swiss army knife of data examination and manipulation
The Swiss army knife of data examination and manipulation.
This is the tool to use when you need to visualize or convert data in different formats.
Think of bo as a data processor that takes in streams of commands and data, and outputs accordingly:
Command line arguments --\ +------+
\ | |
Files -----------------------> | bo | ---> stdout or file
/ | |
stdin -------------------/ +------+
Input consists of whitespace separated commands and data, which can be passed in via command line arguments, files, and stdin. Regardless of input source(s), the syntax is exactly the same: whitespace separated commands. Commands determine how input data is to be interpreted, and how output is to be formatted.
This is a short introduction to understand the examples. See the Commands
section below for a full description.
The values i, o, P, and "a string" are used to initiate commands. If bo doesn't recognize a command initiator, it will attempt to interpret numeric data.
For example, ih2b
means set input type to hexadecimal, 2 bytes per value, big endian. oo4l11
means set output type to octal, 4 bytes per value, little endian, 11 digits minimum.
oh1l2 Ps if4l 35.01 10.5 ih1 9f 5a
This stream of commands does the following:
And output will be 3d 0a 0c 42 00 00 28 41 9f 5a
Note: the -n
flag just appends a newline after processing.
$ bo -n "oh1 Ps ih4b 12345678"
12 34 56 78
$ bo -n "oh1 Ps ih4l 12345678"
78 56 34 12
$ bo -n "oh8b16 Pc ii8b 1000000"
0x00000000000f4240
$ bo -n "oi8b ih8b 7fffffffffffffff"
9223372036854775807
$ bo -n "ob8b ih8b ffe846134453a8c2"
1111111111101000010001100001001101000100010100111010100011000010
Fake a 12-byte memory dump as an example:
$ bo "oB1 if4b 1.1 8.5 305.125" >memory_dump.bin
What it looks like in memory:
$ bo -n -i memory_dump.bin "oh1b2 Pc iB1"
0x3f, 0x8c, 0xcc, 0xcd, 0x41, 0x08, 0x00, 0x00, 0x43, 0x98, 0x90, 0x00
What it looks like interpreted as floats:
$ bo -n -i memory_dump.bin "of4b3 Pc iB1"
1.100, 8.500, 305.125
$ bo "oB1 ih2b 0123 4567 89ab" >data.bin
$ bo -n -i data.bin "oh2l Pc iB1"
0x2301, 0x6745, 0xab89
Note: Once input type is set to binary, bo stops parsing, and just passes input DIRECTLY to the output system using the current output type.
example.txt:
This is a test.
There are tabs between these words.
¡8-Ⅎ⊥∩ sʇɹoddns osʃɐ ʇI
Read in example.txt as raw binary, and output as a C-style escaped string.
$ bo -n -i example.txt "os iB1"
This is a test.\nThere are tabs\tbetween\tthese\twords.\n¡8-Ⅎ⊥∩ sʇɹoddns osʃɐ ʇI
$ bo -n "oi4b if4b 1.5"
1069547520
$ bo -n "oh1l2 Pc if4b 1.5 1.25 ii2b 1000 2000 3000 ih1 ff fe 7a ib1 10001011"
Does the following:
oh1l2
).Pc
).if4b 1.5 1.25
).ii2b 1000 2000 3000
)ih1 ff fe 7a
)ib1 10001011
)Results in:
0x3f, 0xc0, 0x00, 0x00, 0x3f, 0xa0, 0x00, 0x00, 0x03, 0xe8, 0x07, 0xd0, 0x0b, 0xb8, 0xff, 0xfe, 0x7a, 0x8b
bo [options] command [command] ...
The bo
command can take input from command line arguments, files (using the -i switch), and stdin (using -i -
). You may specify as many -i
switches as you like. Bo first reads all command line arguments, and then reads files in the order they were specified using the -i
switch.
For example:
bo -i file1.txt -i - -i file2.txt "oh2b4 Pc" io4b "1 2 3"
Bo will:
oh2b4 Pc
(set output type to 2-byte hex, big endian, min 4 digits, then use "c" preset)io4b
(set input type to 4-byte octal, big endian)1 2 3
(read 3 integers)file1.txt
(execute all commands contained in the file)-i -
) (execute all commands passed in via stdin)file2.txt
(execute all commands contained in the file)By default, bo outputs to stdout, but you can specify an output file using -o
.
Bo parses whitespace separated strings and interprets them as commands. The following commands are supported:
Data is interpreted according to the input format, stored in an intermediary buffer as binary data, and then later re-interpreted and printed according to the output format.
Strings are copied as bytes to the intermediary binary buffer, to be reinterpreted later by the output format.
Before processing any data, both input and output types must be specified via input type
and output type
commands. You may later change the input or output types again via commands, even after interpreting data. Any time the output type is changed, all buffers are flushed.
The input specifier command consists of 3 parts:
The output specifier command consists of 4 parts:
Determines the type to be used for interpreting incoming data, or presenting outgoing data.
As the boolean type operates on sub-byte values, its behavior may seem surprising at first. In little endian systems, not only does byte 0 occur first in a word, but so does bit 0. Bo honors this bit ordering when printing. For example:
$ bo ob2b ih2b 6
0000000000000110
$ bo ob2l ih2l 6
0110000000000000
Note, however, that when inputting textual representations of boolean values, they are ALWAYS read as big endian (same as with all other types), and then stored according to the input endianness:
$ bo ob2b ib2b 1011
0000000000001011
$ bo ob2l ib2l 1011
1101000000000000
$ bo ob2b ib2l 1011
0000101100000000
$ bo ob2l ib2b 1011
0000000011010000
If you set the input type to raw binary (B), bo will no longer parse input; rather, it will stream raw input directly to the output function.
Determines how wide of a data field to store the data in:
Determines in which order bits and bytes are encoded:
The endianness field is optional when data width is 1, EXCEPT for output type boolean.
Specifies the minimum number of digits to print when outputting numeric values. This is an optional field, and defaults to 1.
For integer types, zeroes are prepended until the printed value has the specified number of digits. For floating point types, zeroes are appended until the fractional portion has the specified number of digits. The whole number portion is not used and has no effect in this calculation.
ih2l
: Input type hexadecimal encoded integer, 2 bytes per value, little endianio4b
: Input type octal encoded integer, 4 bytes per value, big endianif4l
: Input type floating point, 4 bytes per value, little endianoi4l
: Output as 4-byte integers in base 10 with default minimum 1 digit (i.e. no zero padding)of8l10
: Interpret data as 8-byte floats and output with 10 digits after the decimal point.Defines what to prefix each output entry with.
Note: Examples have escaped quotes since that's usually what you'll be doing with command line arguments.
p\"0x\"
: Prefix all values with "0x".p\"The next value is: \"
: Prefix all values with "The next value is: "Defines what to suffix each output entry with (except for the last entry).
Note: Examples have escaped quotes since that's usually what you'll be doing with command line arguments.
s\", \"
: Suffix all values with ", ".s\" | \"
: Suffix all values with " | "Presets define preset prefixes and suffixes for common tasks. Currently, the following are supported:
Requirements:
Commands:
mkdir build
cd build
cmake ..
make
./bo_app/bo -h
./libbo/test/libbo_test
All of bo's functionality is in the library libbo. The API is small (4 calls, 2 callbacks) and pretty straightforward since all commands and configurations are done through the parsed data. The basic process is:
test_helpers.cpp
shows how to parse strings, and main.c
from bo_app shows how to use file streams.
Copyright 2018 Karl Stenerud
Released under MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.