Applied Pokology
Back to blog...
_____
---' __\_______
______) Multi-line output in poke pretty-printers
__)
__)
---._______)
Jose E. Marchesi
May 3, 2020
The ID3V1 tag format describes the format for the tags that are
embeded in MP3 files, giving information about the song stored in the
file, such as genre, the name of the artist, and so on. While hacking
the id3v1 pickle today, I found a little dilemma on how to best
present a pretty-printed version of a tag to the user.
This tag format is very rudimentary, and stores strings as arrays of
characters of fixed sizes, which are not NULL-terminated:
,----
| type ID3V1_Tag =
| struct
| {
| char[3] id = ['T', 'A', 'G'];
| char[30] title;
| char[30] artist;
| char[30] album;
| char[4] year;
|
| union
| {
| /* ID3v1.1 */
| struct
| {
| char[28] comment;
| byte zero = 0;
| byte track : track != 0;
| } extended;
| /* ID3v1 */
| char[30] comment;
| } data;
| ...
| };
`----
So, as you can imagine, opening a MP3 file and looking at the tag (which
is located 128#B from the end of the file) is not very revealing at
first sight:
,----
| (poke) ID3V1_Tag @ (iosize (get_ios) - 128#B)
| ID3V1_Tag {
| id=[0x54UB,0x41UB,0x47UB],
| title=[0x30UB,0x31UB,0x20UB,0x2dUB,0x20UB,...],
| artist=[0x4aUB,0x6fUB,0x61UB,0x71UB,0x75UB,...],
| album=[0x4dUB,0x65UB,0x6eUB,0x74UB,0x69UB,...],
| year=[0x20UB,0x20UB,0x20UB,0x20UB],
| data=struct {
| extended=struct {
| comment=[0x20UB,0x20UB,0x20UB,0x20UB,0x20UB,...],
| zero=0x0UB,
| track=0x1UB
| }
| },
| genre=0xffUB
| }
`----
Fortunately, poke supports pretty printers. If a struct type has a
method called _print, it will be used by poke as a pretty printer if the
`pretty-print' option is set:
,----
| (poke) .set pretty-print yes
`----
We have a convention already for the output emitted by pretty-printers:
pretty printed values always start with `#<' and end with `>'. For
example, a pretty-printed BPF register from a BPF instruction:
,----
| (poke) BPF_Insn @ ...
| BPF_Insn = {
| ...
| regs=BPF_Regs {
| src=#<%r3>,
| dst=#<%r0>
| }
| ...
| }
`----
The #< > convention was originally adopted to make it explicit for
the user that everything she sees between #< > is pretty-printed,
and does _not_ necessarily reflect the physical structure of the data.
Also some information may be missing. In order to get an exact and
complete description of the data, the user should .set pretty-print and
evaluate the value again at the prompt.
This works well for written representations that span just for a single
line. But, in the case of the MP3 tag above, including all the
information in a single line is annoying.
So I am suggesting to extend the convention: in case you want the
pretty-printed representation to span for more than one line, you should
place first the #< starting at the column 0, then the lines with the
data, and finally > in its own line starting at column 0.
I used this convention for id3v1 tags, and this is the result:
,----
| (poke) .file eclipse.mp3
| The current IOS is now `./eclipse.mp3'.
| (poke) load id3v1
| (poke) ID3V1_Tag @ (iosize (get_ios) - 128#B)
| #<
| genre: 255
| title: 01 - Eclipse De Mar
| artist: Joaquin Sabina
| album: Mentiras Piadosas
| year:
| comment:
| track: 1
| >
`----
See the file `pickles/id3v1.pk' in the poke sources if you are curious
about the implementation of this simple pickle, including its
pretty-printer method.
Happy poking! :)