diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml new file mode 100644 index 000000000..2ff213402 --- /dev/null +++ b/.github/workflows/manual.yml @@ -0,0 +1,40 @@ +name: Update github pages manual +on: + push: + branches: + - master + paths: + - 'doc/fq.1.adoc' +concurrency: gh-pages + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v7 + - name: Install asciidoc + run: apt install -y asciidoc + - name: Build website + run: | + make doc/fq.1.html + mkdir site + mv doc/fq.1.html site/index.html + - name: Upload pages artifact + uses: actions/upload-pages-artifact@v5 + with: + path: site + + deploy: + needs: build + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + uses: actions/deploy-pages@v5 + id: deployment diff --git a/Makefile b/Makefile index 0b80e3033..c5a32b50b 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,8 @@ doc: $(wildcard *.md doc/*.md) @doc/mdsh.sh ./fq $@ doc/%.svg.sh: fq - (cd doc ; ../$@ ../fq) | go run github.com/wader/ansisvg@master > $(@:.svg.sh=.svg) + (cd doc ; ../$@ "../fq -C -o width=80 -o unicode=true") | go run github.com/wader/ansisvg@master > $(@:.svg.sh=.svg) + (cd doc ; ../$@ "../fq -M -o width=80") > $(@:.svg.sh=.txt) doc/formats.svg: fq @# ignore graphviz version as it causes diff when nothing has changed @@ -56,6 +57,15 @@ doc/file.mp3: Makefile doc/file.mp4: Makefile ffmpeg -y -f lavfi -i sine -f lavfi -i testsrc -c:a aac -c:v h264 -f mp4 -t 20ms "$@" +doc/fq.1.adoc: fq + cd doc && (../fq -Rrs -L . 'include "fq.1.tmpl.adoc"; fq_tmpl_adoc(true)' fq.1.tmpl.adoc > fq.1.adoc) + +doc/fq.1: doc/fq.1.adoc + asciidoctor -b manpage doc/fq.1.adoc > doc/fq.1 + +doc/fq.1.html: doc/fq.1.adoc + asciidoctor -b html5 -a webfonts! doc/fq.1.adoc > doc/fq.1.html + gogenerate: always go generate -x ./... diff --git a/README.md b/README.md index f8b4e4d9c..954ae9a14 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,22 @@ # fq -Tool, language and decoders for working with binary data. - -TLDR: it aims to be jq, hexdump, dd and gdb for files combined into one. +Tool, language and decoders for working with binary formats. ![fq demo](doc/demo.svg) Basic usage is `fq . file`, `fq d file` or `fq 'some query' file ...`. -For details see [usage.md](doc/usage.md). +For details see [the manual](https://wader.github.io/fq/), also available as `man fq` and as [AsciiDoc](doc/fq.1.adoc). ### Background -fq is inspired by the [jq](https://jqlang.github.io/jq/) tool and language and allows you to work with binary formats in the same way. In addition to using jq expressions it can also present decoded tree structures, transform, slice and concatenate binary data. It also supports nested formats and features an interactive REPL with auto-completion of functions and names. +fq is a tool, language, and decoders for working with binary formats and data. +In most cases it behaves and feels similar to [jq](https://jqlang.github.io/jq/) and it +also uses the same expression language. +To get the most out of fq it's recommended to learn more about jq. -It was originally designed to query, inspect and debug media codecs and containers like MP4, FLAC and JPEG but has since been extended to support a variety of formats like executables, packet captures (with TCP reassembly) and serialization formats like JSON, YAML, XML, CBOR, protobuf. In addition it also has functions to work with URLs, convert to/from hex, number bases, search for patterns etc. +It features a structural hex viewer, nested format decoding, slicing and concatenating +binary data, bit-level decoding and an interactive REPL with auto-completion. ### Goals @@ -32,65 +34,65 @@ It was originally designed to query, inspect and debug media codecs and containe [fq -rn -L doc 'include "formats"; formats_list']: sh-start -[aac_frame](doc/formats.md#aac_frame), +aac_frame, adts, adts_frame, aiff, amf0, apev2, -[apple_bookmark](doc/formats.md#apple_bookmark), +apple_bookmark, ar, -[asn1_ber](doc/formats.md#asn1_ber), +asn1_ber, av1_ccr, av1_frame, av1_obu, avc_annexb, -[avc_au](doc/formats.md#avc_au), +avc_au, avc_dcr, -[avc_nalu](doc/formats.md#avc_nalu), +avc_nalu, avc_pps, -[avc_sei](doc/formats.md#avc_sei), +avc_sei, avc_sps, -[avi](doc/formats.md#avi), -[avro_ocf](doc/formats.md#avro_ocf), -[bencode](doc/formats.md#bencode), +avi, +avro_ocf, +bencode, bitcoin_blkdat, -[bitcoin_block](doc/formats.md#bitcoin_block), +bitcoin_block, bitcoin_script, bitcoin_transaction, -[bits](doc/formats.md#bits), -[bplist](doc/formats.md#bplist), +bits, +bplist, bsd_loopback_frame, -[bson](doc/formats.md#bson), -[bytes](doc/formats.md#bytes), +bson, +bytes, bzip2, -[caff](doc/formats.md#caff), -[cbor](doc/formats.md#cbor), -[csv](doc/formats.md#csv), +caff, +cbor, +csv, dns, dns_tcp, elf, ether8023_frame, exif, fairplay_spc, -[fit](doc/formats.md#fit), +fit, flac, -[flac_frame](doc/formats.md#flac_frame), +flac_frame, flac_metadatablock, flac_metadatablocks, flac_picture, flac_streaminfo, gif, gzip, -[heif](doc/formats.md#heif), +heif, hevc_annexb, -[hevc_au](doc/formats.md#hevc_au), +hevc_au, hevc_dcr, hevc_nalu, hevc_pps, hevc_sps, hevc_vps, -[html](doc/formats.md#html), +html, icc_profile, icmp, icmpv6, @@ -103,57 +105,57 @@ jp2c, jpeg, json, jsonl, -[leveldb_descriptor](doc/formats.md#leveldb_descriptor), -[leveldb_log](doc/formats.md#leveldb_log), -[leveldb_table](doc/formats.md#leveldb_table), -[luajit](doc/formats.md#luajit), -[macho](doc/formats.md#macho), +leveldb_descriptor, +leveldb_log, +leveldb_table, +luajit, +macho, macho_fat, -[markdown](doc/formats.md#markdown), -[matroska](doc/formats.md#matroska), -[midi](doc/formats.md#midi), -[moc3](doc/formats.md#moc3), -[mp3](doc/formats.md#mp3), +markdown, +matroska, +midi, +moc3, +mp3, mp3_frame, mp3_frame_vbri, mp3_frame_xing, -[mp4](doc/formats.md#mp4), +mp4, mpeg_asc, mpeg_es, mpeg_pes, mpeg_pes_packet, mpeg_spu, mpeg_ts, -[msgpack](doc/formats.md#msgpack), -[negentropy](doc/formats.md#negentropy), -[nes](doc/formats.md#nes), +msgpack, +negentropy, +nes, ogg, ogg_page, -[opentimestamps](doc/formats.md#opentimestamps), +opentimestamps, opus_packet, -[pcap](doc/formats.md#pcap), +pcap, pcapng, -[pg_btree](doc/formats.md#pg_btree), -[pg_control](doc/formats.md#pg_control), -[pg_heap](doc/formats.md#pg_heap), +pg_btree, +pg_control, +pg_heap, png, prores_frame, -[protobuf](doc/formats.md#protobuf), +protobuf, protobuf_widevine, pssh_playready, -[rtmp](doc/formats.md#rtmp), +rtmp, safetensors, sll2_packet, sll_packet, -[stl](doc/formats.md#stl), -[tap](doc/formats.md#tap), +stl, +tap, tar, tcp_segment, tiff, -[tls](doc/formats.md#tls), +tls, toml, -[tzif](doc/formats.md#tzif), -[tzx](doc/formats.md#tzx), +tzif, +tzx, udp_datagram, vorbis_comment, vorbis_packet, @@ -161,18 +163,18 @@ vp8_frame, vp9_cfm, vp9_frame, vpx_ccr, -[wasm](doc/formats.md#wasm), +wasm, wav, webp, -[xml](doc/formats.md#xml), +xml, yaml, -[zip](doc/formats.md#zip) +zip [#]: sh-end It can also work with some common text formats like URLs, hex, base64, PEM etc and for some serialization formats like XML, YAML, etc. it can transform both from and to jq values. -For details see [formats.md](doc/formats.md) and [usage.md](doc/usage.md). +For details see [the manual](https://wader.github.io/fq/). ## Presentations and media diff --git a/doc/demo.svg b/doc/demo.svg index ec8a2aa64..e08b8cbc7 100644 --- a/doc/demo.svg +++ b/doc/demo.svg @@ -29,30 +29,30 @@ # Overview of mp3 file $ fq . file.mp3 -00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f0123456789abcdef│.{}: file.mp3 (mp3) -0x000│49 44 33 04 00 00 00 00 15 39 54 53 53 45 00 00│ID3......9TSSE..headers[0:1]: -* │until 0xac2.7 (2755) │ │ -0xac0ff fb 40 c0 00 00 00 00 00 00 00 00 00..@..........frames[0:3]: -0xad000 00 00 00 00 00 00 00 49 6e 66 6f 00 00 00 0f........Info.... -* │until 0xd19.7 (end) (599) │ │ - │ │ │ footers[0:0]: +00 01 02 03 04 05 06 07 08 090123456789│.{}: file.mp3 (mp3) +0x000│49 44 33 04 00 00 00 00 15 39│ID3......9│ headers[0:1]: +* │until 0xac2.7 (2755) │ │ +0xabeff fb 40 c0 00..@..frames[0:3]: +0xac800 00 00 00 00 00 00 00 00 00.......... +* │until 0xd19.7 (end) (599) │ │ + │ │ │ footers[0:0]: # Show header of first ID3v2 tag inside mp3 file $ fq '.headers[0].header' file.mp3 -00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f0123456789abcdef│.headers[0].header{}: -0x0│49 44 33 │ID3 │ magic: "ID3" (valid) -0x004 . version: 4 (valid) -0x000 . revision: 0 -0x000 . flags{}: -0x000 00 15 39 │ ...9 │ size: 2745 +00 01 02 03 04 05 06 07 08 090123456789│.headers[0].header{}: +0x0│49 44 33 │ID3 │ magic: "ID3" (valid) +0x004 . version: 4 (valid) +0x000 . revision: 0 +0x000 . flags{}: +0x000 00 15 39│ ...9│ size: 2745 # Show encoder software used $ fq -Vr '.frames[0].tag.encoder' file.mp3 Lavc58.13 # Decode at two offsets as mp3_frame and show bitrate $ fq -d bytes '.[0xb79,0xc49:] | mp3_frame.header.bitrate' file.mp3 -00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f0123456789abcdef -0xb70│ 50 │ P │.header.bitrate: 64000 (5) -00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f0123456789abcdef -0xc40│ 52 │ R │.header.bitrate: 64000 (5) +00 01 02 03 04 05 06 07 08 090123456789 +0xb72│ 50│ P│.header.bitrate: 64000 (5) +00 01 02 03 04 05 06 07 08 090123456789 +0xc44│ 52 │ R │.header.bitrate: 64000 (5) # Extract PNG file $ fq '.headers[].frames[] | select(.id == "APIC")?.picture | tobits' file.mp3 >file.png $ file file.png diff --git a/doc/demo.txt b/doc/demo.txt new file mode 100644 index 000000000..76c732d71 --- /dev/null +++ b/doc/demo.txt @@ -0,0 +1,50 @@ +# Overview of mp3 file +$ fq . file.mp3 + │00 01 02 03 04 05 06 07 08 09│0123456789│.{}: file.mp3 (mp3) +0x000│49 44 33 04 00 00 00 00 15 39│ID3......9│ headers[0:1]: +* │until 0xac2.7 (2755) │ │ +0xabe│ ff fb 40 c0 00│ ..@..│ frames[0:3]: +0xac8│00 00 00 00 00 00 00 00 00 00│..........│ +* │until 0xd19.7 (end) (599) │ │ + │ │ │ footers[0:0]: + +# Show header of first ID3v2 tag inside mp3 file +$ fq '.headers[0].header' file.mp3 + │00 01 02 03 04 05 06 07 08 09│0123456789│.headers[0].header{}: +0x0│49 44 33 │ID3 │ magic: "ID3" (valid) +0x0│ 04 │ . │ version: 4 (valid) +0x0│ 00 │ . │ revision: 0 +0x0│ 00 │ . │ flags{}: +0x0│ 00 00 15 39│ ...9│ size: 2745 + +# Show encoder software used +$ fq -Vr '.frames[0].tag.encoder' file.mp3 +Lavc58.13 + +# Decode at two offsets as mp3_frame and show bitrate +$ fq -d bytes '.[0xb79,0xc49:] | mp3_frame.header.bitrate' file.mp3 + │00 01 02 03 04 05 06 07 08 09│0123456789│ +0xb72│ 50│ P│.header.bitrate: 64000 (5) + │00 01 02 03 04 05 06 07 08 09│0123456789│ +0xc44│ 52 │ R │.header.bitrate: 64000 (5) + +# Extract PNG file +$ fq '.headers[].frames[] | select(.id == "APIC")?.picture | tobits' file.mp3 >file.png +$ file file.png +file.png: PNG image data, 320 x 240, 8-bit/color RGB, non-interlaced + +# Grep for PNG header, extract resolution and output as YAML +$ fq -r 'grep_by(.type == "IHDR") | {res: {width, height}} | to_yaml' file.mp3 +res: + height: 240 + width: 320 + +# Add query parameter to URL +$ echo 'http://host?a=b' | fq -Rr 'from_url | .query.b = "a b c" | to_url' +http://host?a=b&b=a+b+c + +# Extract JSON and base64 encoded query parameter p +$ echo 'https://host?p=eyJhIjoiaGVsbG8ifQ%3D%3D' | fq -R 'from_url.query.p | from_base64 | fromjson' +{ + "a": "hello" +} diff --git a/doc/dev.md b/doc/dev.md index cd760fabd..bb311af30 100644 --- a/doc/dev.md +++ b/doc/dev.md @@ -48,7 +48,7 @@ Flags can be struct with bit-fields. - No heading section is needs with format name, will be added by `make doc` and fq cli help system. - Add a `testdata/_help.fqtest` with just `$ fq -h ` to test CLI help. - If in doubt look at `mp4.md`/`mp4.go` etc. - - Run `make README.md doc/formats.md` to update md files. + - Run `make README.md doc/fq.1.adoc doc/fq.1` to update md files. - Run linter `make lint` - Run fuzzer `make fuzz GROUP=`, see usage in Makefile diff --git a/doc/display_decode_value.svg b/doc/display_decode_value.svg index e3973a417..0cb8446a9 100644 --- a/doc/display_decode_value.svg +++ b/doc/display_decode_value.svg @@ -1,4 +1,4 @@ - + $ fq 'first(grep_by(.type == "elst"))' file.mp4 -00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f0123456789abcdef│.boxes[3].boxes[1].boxes[1].boxes[0]{}: box -0xd6000 00 00 1c .... size: 28 -0xd60│ 65 6c 73 74 │ elst │ type: "elst" (An edit list) -0xd6000 . version: 0 -0xd6000 00..flags: 0 -0xd7000 . -0xd7000 00 00 01 .... entry_count: 1 -0xd7000 00 00 28 00 00 00 00 00 01 00...(.......entries[0:1]: -0xd8000 . +00 01 02 03 04 05 06 07 08 090123456789│.boxes[3].boxes[1].boxes[1].boxes[0]{}: box +0xd5c00.size: 28 +0xd6600 00 1c ... +0xd66│ 65 6c 73 74 │ elst │ type: "elst" (An edit list) +0xd6600 . version: 0 +0xd6600 00..flags: 0 +0xd7000 . +0xd7000 00 00 01 .... entry_count: 1 +0xd7000 00 00 28 00...(.entries[0:1]: +0xd7a00 00 00 00 01 00 00 ....... diff --git a/doc/display_decode_value.svg.sh b/doc/display_decode_value.svg.sh index 9320a741b..de1460632 100755 --- a/doc/display_decode_value.svg.sh +++ b/doc/display_decode_value.svg.sh @@ -4,7 +4,7 @@ FQ="$1" s() { echo "\$ $1" - sh -c "${1/fq/$FQ -o unicode=true -C}" + sh -c "${1/fq/$FQ}" } s "fq 'first(grep_by(.type == \"elst\"))' file.mp4" diff --git a/doc/display_decode_value.txt b/doc/display_decode_value.txt new file mode 100644 index 000000000..e476809ed --- /dev/null +++ b/doc/display_decode_value.txt @@ -0,0 +1,11 @@ +$ fq 'first(grep_by(.type == "elst"))' file.mp4 + |00 01 02 03 04 05 06 07 08 09|0123456789|.boxes[3].boxes[1].boxes[1].boxes[0]{}: box +0xd5c| 00| .| size: 28 +0xd66|00 00 1c |... | +0xd66| 65 6c 73 74 | elst | type: "elst" (An edit list) +0xd66| 00 | . | version: 0 +0xd66| 00 00| ..| flags: 0 +0xd70|00 |. | +0xd70| 00 00 00 01 | .... | entry_count: 1 +0xd70| 00 00 00 28 00| ...(.| entries[0:1]: +0xd7a|00 00 00 00 01 00 00 |....... | diff --git a/doc/display_decode_value_d.svg b/doc/display_decode_value_d.svg index d999bb89f..feda3175b 100644 --- a/doc/display_decode_value_d.svg +++ b/doc/display_decode_value_d.svg @@ -1,4 +1,4 @@ - + $ fq 'first(grep_by(.type == "elst")) | d' file.mp4 -00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f0123456789abcdef│.boxes[3].boxes[1].boxes[1].boxes[0]{}: box -0xd6000 00 00 1c .... size: 28 -0xd60│ 65 6c 73 74 │ elst │ type: "elst" (An edit list) -0xd6000 . version: 0 -0xd6000 00..flags: 0 -0xd7000 . -0xd7000 00 00 01 .... entry_count: 1 - │ │ │ entries[0:1]: - │ │ │ [0]{}: entry -0xd7000 00 00 28 │ ...( │ segment_duration: 40 -0xd7000 00 00 00 .... media_time: 0 -0xd7000 01 00...media_rate: 1 -0xd8000 . +00 01 02 03 04 05 06 07 08 090123456789│.boxes[3].boxes[1].boxes[1].boxes[0]{}: box +0xd5c00.size: 28 +0xd6600 00 1c ... +0xd66│ 65 6c 73 74 │ elst │ type: "elst" (An edit list) +0xd6600 . version: 0 +0xd6600 00..flags: 0 +0xd7000 . +0xd7000 00 00 01 .... entry_count: 1 + │ │ │ entries[0:1]: + │ │ │ [0]{}: entry +0xd7000 00 00 28 │ ...( │ segment_duration: 40 +0xd7000.media_time: 0 +0xd7a00 00 00 ... +0xd7a00 01 00 00 .... media_rate: 1 diff --git a/doc/display_decode_value_d.svg.sh b/doc/display_decode_value_d.svg.sh index e0ab47141..e031bc602 100755 --- a/doc/display_decode_value_d.svg.sh +++ b/doc/display_decode_value_d.svg.sh @@ -4,7 +4,7 @@ FQ="$1" s() { echo "\$ $1" - sh -c "${1/fq/$FQ -o unicode=true -C}" + sh -c "${1/fq/$FQ}" } s "fq 'first(grep_by(.type == \"elst\")) | d' file.mp4" diff --git a/doc/display_decode_value_d.txt b/doc/display_decode_value_d.txt new file mode 100644 index 000000000..fb420b5cb --- /dev/null +++ b/doc/display_decode_value_d.txt @@ -0,0 +1,15 @@ +$ fq 'first(grep_by(.type == "elst")) | d' file.mp4 + |00 01 02 03 04 05 06 07 08 09|0123456789|.boxes[3].boxes[1].boxes[1].boxes[0]{}: box +0xd5c| 00| .| size: 28 +0xd66|00 00 1c |... | +0xd66| 65 6c 73 74 | elst | type: "elst" (An edit list) +0xd66| 00 | . | version: 0 +0xd66| 00 00| ..| flags: 0 +0xd70|00 |. | +0xd70| 00 00 00 01 | .... | entry_count: 1 + | | | entries[0:1]: + | | | [0]{}: entry +0xd70| 00 00 00 28 | ...( | segment_duration: 40 +0xd70| 00| .| media_time: 0 +0xd7a|00 00 00 |... | +0xd7a| 00 01 00 00 | .... | media_rate: 1 diff --git a/doc/display_decode_value_dv.svg b/doc/display_decode_value_dv.svg index 78a758fb4..a4a7a105b 100644 --- a/doc/display_decode_value_dv.svg +++ b/doc/display_decode_value_dv.svg @@ -1,4 +1,4 @@ - + $ fq 'first(grep_by(.type == "elst")) | dv' file.mp4 -00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f0123456789abcdef│.boxes[3].boxes[1].boxes[1].boxes[0]{}: box 0xd65-0xd81 (28) -0xd6000 00 00 1c .... size: 28 0xd65-0xd69 (4) -0xd60│ 65 6c 73 74 │ elst │ type: "elst" (An edit list) 0xd69-0xd6d (4) -0xd6000 . version: 0 0xd6d-0xd6e (1) -0xd6000 00..flags: 0 0xd6e-0xd71 (3) -0xd7000 . -0xd7000 00 00 01 .... entry_count: 1 0xd71-0xd75 (4) - │ │ │ entries[0:1]: 0xd75-0xd81 (12) - │ │ │ [0]{}: entry 0xd75-0xd81 (12) -0xd7000 00 00 28 │ ...( │ segment_duration: 40 0xd75-0xd79 (4) -0xd7000 00 00 00 .... media_time: 0 0xd79-0xd7d (4) -0xd7000 01 00...media_rate: 1 0xd7d-0xd81 (4) -0xd8000 . +00 01 02 03 04 05 06 07 08 090123456789│.boxes[3].boxes[1].boxes[1].boxes[0]{}: box 0xd65-0xd81 (28) +0xd5c00.size: 28 0xd65-0xd69 (4) +0xd6600 00 1c ... +0xd66│ 65 6c 73 74 │ elst │ type: "elst" (An edit list) 0xd69-0xd6d (4) +0xd6600 . version: 0 0xd6d-0xd6e (1) +0xd6600 00..flags: 0 0xd6e-0xd71 (3) +0xd7000 . +0xd7000 00 00 01 .... entry_count: 1 0xd71-0xd75 (4) + │ │ │ entries[0:1]: 0xd75-0xd81 (12) + │ │ │ [0]{}: entry 0xd75-0xd81 (12) +0xd7000 00 00 28 │ ...( │ segment_duration: 40 0xd75-0xd79 (4) +0xd7000.media_time: 0 0xd79-0xd7d (4) +0xd7a00 00 00 ... +0xd7a00 01 00 00 .... media_rate: 1 0xd7d-0xd81 (4) diff --git a/doc/display_decode_value_dv.svg.sh b/doc/display_decode_value_dv.svg.sh index 3b15646a5..291507d36 100755 --- a/doc/display_decode_value_dv.svg.sh +++ b/doc/display_decode_value_dv.svg.sh @@ -4,7 +4,7 @@ FQ="$1" s() { echo "\$ $1" - sh -c "${1/fq/$FQ -o unicode=true -C}" + sh -c "${1/fq/$FQ}" } s "fq 'first(grep_by(.type == \"elst\")) | dv' file.mp4" diff --git a/doc/display_decode_value_dv.txt b/doc/display_decode_value_dv.txt new file mode 100644 index 000000000..0819d9a3f --- /dev/null +++ b/doc/display_decode_value_dv.txt @@ -0,0 +1,15 @@ +$ fq 'first(grep_by(.type == "elst")) | dv' file.mp4 + |00 01 02 03 04 05 06 07 08 09|0123456789|.boxes[3].boxes[1].boxes[1].boxes[0]{}: box 0xd65-0xd81 (28) +0xd5c| 00| .| size: 28 0xd65-0xd69 (4) +0xd66|00 00 1c |... | +0xd66| 65 6c 73 74 | elst | type: "elst" (An edit list) 0xd69-0xd6d (4) +0xd66| 00 | . | version: 0 0xd6d-0xd6e (1) +0xd66| 00 00| ..| flags: 0 0xd6e-0xd71 (3) +0xd70|00 |. | +0xd70| 00 00 00 01 | .... | entry_count: 1 0xd71-0xd75 (4) + | | | entries[0:1]: 0xd75-0xd81 (12) + | | | [0]{}: entry 0xd75-0xd81 (12) +0xd70| 00 00 00 28 | ...( | segment_duration: 40 0xd75-0xd79 (4) +0xd70| 00| .| media_time: 0 0xd79-0xd7d (4) +0xd7a|00 00 00 |... | +0xd7a| 00 01 00 00 | .... | media_rate: 1 0xd7d-0xd81 (4) diff --git a/doc/display_decode_value_dv2.svg b/doc/display_decode_value_dv2.svg index 8b99a6f38..3455b79ab 100644 --- a/doc/display_decode_value_dv2.svg +++ b/doc/display_decode_value_dv2.svg @@ -1,4 +1,4 @@ - + $ fq '.frames[1].header | dv' file.mp3 -00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f0123456789abcdef│.frames[1].header{}: 0xb79-0xb7d (4) -0xb70ff fb .. sync: 0b11111111111 (valid) 0xb79-0xb7a.3 (1.3) -0xb70fb . mpeg_version: "1" (3) (MPEG Version 1) 0xb7a.3-0xb7a.5 (0.2) -0xb70fb . layer: 3 (1) (MPEG Layer 3) 0xb7a.5-0xb7a.7 (0.2) - │ │ │ sample_count: 1152 -0xb70fb . protection_absent: true (No CRC) 0xb7a.7-0xb7b (0.1) -0xb70│ 50 │ P │ bitrate: 64000 (5) 0xb7b-0xb7b.4 (0.4) -0xb70│ 50 │ P │ sample_rate: 44100 (0) 0xb7b.4-0xb7b.6 (0.2) -0xb70│ 50 │ P │ padding: "not_padded" (0b0) 0xb7b.6-0xb7b.7 (0.1) -0xb70│ 50 │ P │ private: 0 0xb7b.7-0xb7c (0.1) -0xb70c4 . channels: "mono" (0b11) 0xb7c-0xb7c.2 (0.2) -0xb70c4 . channel_mode: "none" (0b0) 0xb7c.2-0xb7c.4 (0.2) -0xb70c4 . copyright: 0 0xb7c.4-0xb7c.5 (0.1) -0xb70c4 . original: 1 0xb7c.5-0xb7c.6 (0.1) -0xb70c4 . emphasis: "none" (0b0) 0xb7c.6-0xb7d (0.2) +00 01 02 03 04 05 06 07 08 090123456789│.frames[1].header{}: 0xb79-0xb7d (4) +0xb72ff fb .. sync: 0b11111111111 (valid) 0xb79-0xb7a.3 (1.3) +0xb72fb . mpeg_version: "1" (3) (MPEG Version 1) 0xb7a.3-0xb7a.5 (0.2) +0xb72fb . layer: 3 (1) (MPEG Layer 3) 0xb7a.5-0xb7a.7 (0.2) + │ │ │ sample_count: 1152 +0xb72fb . protection_absent: true (No CRC) 0xb7a.7-0xb7b (0.1) +0xb72│ 50│ P│ bitrate: 64000 (5) 0xb7b-0xb7b.4 (0.4) +0xb72│ 50│ P│ sample_rate: 44100 (0) 0xb7b.4-0xb7b.6 (0.2) +0xb72│ 50│ P│ padding: "not_padded" (0b0) 0xb7b.6-0xb7b.7 (0.1) +0xb72│ 50│ P│ private: 0 0xb7b.7-0xb7c (0.1) +0xb7cc4 . channels: "mono" (0b11) 0xb7c-0xb7c.2 (0.2) +0xb7cc4 . channel_mode: "none" (0b0) 0xb7c.2-0xb7c.4 (0.2) +0xb7cc4 . copyright: 0 0xb7c.4-0xb7c.5 (0.1) +0xb7cc4 . original: 1 0xb7c.5-0xb7c.6 (0.1) +0xb7cc4 . emphasis: "none" (0b0) 0xb7c.6-0xb7d (0.2) diff --git a/doc/display_decode_value_dv2.svg.sh b/doc/display_decode_value_dv2.svg.sh index 9cda6cfe1..9a0563f57 100755 --- a/doc/display_decode_value_dv2.svg.sh +++ b/doc/display_decode_value_dv2.svg.sh @@ -4,7 +4,7 @@ FQ="$1" s() { echo "\$ $1" - sh -c "${1/fq/$FQ -o unicode=true -C}" + sh -c "${1/fq/$FQ}" } s "fq '.frames[1].header | dv' file.mp3" diff --git a/doc/display_decode_value_dv2.txt b/doc/display_decode_value_dv2.txt new file mode 100644 index 000000000..d0a9aafd1 --- /dev/null +++ b/doc/display_decode_value_dv2.txt @@ -0,0 +1,16 @@ +$ fq '.frames[1].header | dv' file.mp3 + |00 01 02 03 04 05 06 07 08 09|0123456789|.frames[1].header{}: 0xb79-0xb7d (4) +0xb72| ff fb | .. | sync: 0b11111111111 (valid) 0xb79-0xb7a.3 (1.3) +0xb72| fb | . | mpeg_version: "1" (3) (MPEG Version 1) 0xb7a.3-0xb7a.5 (0.2) +0xb72| fb | . | layer: 3 (1) (MPEG Layer 3) 0xb7a.5-0xb7a.7 (0.2) + | | | sample_count: 1152 +0xb72| fb | . | protection_absent: true (No CRC) 0xb7a.7-0xb7b (0.1) +0xb72| 50| P| bitrate: 64000 (5) 0xb7b-0xb7b.4 (0.4) +0xb72| 50| P| sample_rate: 44100 (0) 0xb7b.4-0xb7b.6 (0.2) +0xb72| 50| P| padding: "not_padded" (0b0) 0xb7b.6-0xb7b.7 (0.1) +0xb72| 50| P| private: 0 0xb7b.7-0xb7c (0.1) +0xb7c|c4 |. | channels: "mono" (0b11) 0xb7c-0xb7c.2 (0.2) +0xb7c|c4 |. | channel_mode: "none" (0b0) 0xb7c.2-0xb7c.4 (0.2) +0xb7c|c4 |. | copyright: 0 0xb7c.4-0xb7c.5 (0.1) +0xb7c|c4 |. | original: 1 0xb7c.5-0xb7c.6 (0.1) +0xb7c|c4 |. | emphasis: "none" (0b0) 0xb7c.6-0xb7d (0.2) diff --git a/doc/display_json.svg.sh b/doc/display_json.svg.sh index 9fdf44051..09ff81c41 100755 --- a/doc/display_json.svg.sh +++ b/doc/display_json.svg.sh @@ -4,7 +4,7 @@ FQ="$1" s() { echo "\$ $1" - sh -c "${1/fq/$FQ -o unicode=true -C}" + sh -c "${1/fq/$FQ}" } s "fq -n '\"hello\"'" diff --git a/doc/display_json.txt b/doc/display_json.txt new file mode 100644 index 000000000..72d78ebfb --- /dev/null +++ b/doc/display_json.txt @@ -0,0 +1,5 @@ +$ fq -n '"hello"' +"hello" + +$ fq -n '"hello" | d' +"hello" diff --git a/doc/formats.jq b/doc/formats.jq index 9c21d1913..778cb27c2 100755 --- a/doc/formats.jq +++ b/doc/formats.jq @@ -1,135 +1,7 @@ #!/usr/bin/env fq -rnf -def code: "`\(.)`"; -def nbsp: gsub(" "; " "); -def has_section($f; $fhelp): $fhelp.notes or $fhelp.examples or $f.decode_in_arg or ((_registry.files[][] | select(.name=="\($f.name).md").data) // false); - def formats_list: [ formats[] as $f | ({} | _help_format_enrich("fq"; $f; false)) as $fhelp - | if has_section($f; $fhelp) then "[\($f.name)](doc/formats.md#\($f.name))" - else $f.name - end + | $f.name ] | join(",\n"); - -def formats_table: - ( [ { name: "Name" - , desc: "Description" - , uses: "Dependencies" - } - , { name: "-" - , desc: "-" - , uses: "-" - } - , ( formats - | to_entries[] - | (_format_func(.key; "_help")? // {}) as $fhelp - | { name: - ( ( .key as $format - | if has_section(.value; $fhelp) then "[\($format | code)](#\($format))" - else $format | code - end - ) - + " " - ) - , desc: (.value.description | nbsp) - , uses: "\((((.value.dependencies | flatten | map(code)) | join(" "))? // ""))" - } - ), - ( [ formats - | to_entries[] - | . as $e - | select(.value.groups) - | .value.groups[] | {key: ., value: $e.key} - ] - | reduce .[] as $e ({}; .[$e.key] += [$e.value]) - | to_entries[] - | { name: ((.key | code) + " ") - , desc: "Group" - , uses: "\(((.value | map(code)) | join(" ")))" - } - ) - ] - | table( - [ .name - , .desc - , .uses - ]; - [ "" - , (.[0] | . as $rc | $rc.string | rpad(" "; $rc.maxwidth | [., .+20] | max)) - , (.[1] | . as $rc | $rc.string | rpad(" "; $rc.maxwidth | [., .+20] | max)) - , .[2].string - , "" - ] | join("|") - ) - ); - -def formats_sections: - ( formats[] as $f - | ((_registry.files[][] | select(.name=="\($f.name).md").data) // false) as $doc - | ({} | _help_format_enrich("fq"; $f; false)) as $fhelp - | select(has_section($f; $fhelp)) - | "## \($f.name)" - , $f.description + "." - , "" - , ($fhelp.notes | if . then ., "" else empty end) - , if $f.decode_in_arg then - ( "### Options" - , "" - , ( [ { name: "Name" - , default: "Default" - , desc: "Description" - } - , { name: "-" - , default: "-" - , desc: "-" - } - , ( $f.decode_in_arg - | to_entries[] as {$key,$value} - | { name: ($key | code) - , default: ($value | tostring) - , desc: $f.decode_in_arg_doc[$key] - } - ) - ] - | table( - [ .name - , .default - , .desc - ]; - [ "" - , (.[0] | . as $rc | $rc.string | rpad(" "; $rc.maxwidth)) - , (.[1] | . as $rc | $rc.string | rpad(" "; $rc.maxwidth)) - , .[2].string - , "" - ] | join("|") - ) - ) - , "" - ) - else empty - end - , if $fhelp.examples then - ( "### Examples" - , "" - , ( $fhelp.examples[] - | "\(.comment)" - , if .shell then - ( "```" - , "$ \(.shell)" - , "```" - ) - elif .expr then - ( "```" - , "... | \(.expr)" - , "```" - ) - else empty - end - , "" - ) - ) - else empty - end - , ($doc // empty) - ); diff --git a/doc/formats.md b/doc/formats.md deleted file mode 100644 index ab1236ec5..000000000 --- a/doc/formats.md +++ /dev/null @@ -1,1828 +0,0 @@ -## Supported formats - -[fq -rn -L . 'include "formats"; formats_table']: sh-start - -|Name |Description |Dependencies| -|- |- |-| -|[`aac_frame`](#aac_frame) |Advanced Audio Coding frame || -|`adts` |Audio Data Transport Stream |`adts_frame`| -|`adts_frame` |Audio Data Transport Stream frame |`aac_frame`| -|`aiff` |Audio Interchange File Format || -|`amf0` |Action Message Format 0 || -|`apev2` |APEv2 metadata tag |`image`| -|[`apple_bookmark`](#apple_bookmark) |Apple BookmarkData || -|`ar` |Unix archive |`probe`| -|[`asn1_ber`](#asn1_ber) |ASN1 BER (basic encoding rules, also CER and DER) || -|`av1_ccr` |AV1 Codec Configuration Record |`av1_obu`| -|`av1_frame` |AV1 frame |`av1_obu`| -|`av1_obu` |AV1 Open Bitstream Unit || -|`avc_annexb` |H.264/AVC Annex B |`avc_nalu`| -|[`avc_au`](#avc_au) |H.264/AVC Access Unit |`avc_nalu`| -|`avc_dcr` |H.264/AVC Decoder Configuration Record |`avc_nalu`| -|[`avc_nalu`](#avc_nalu) |H.264/AVC Network Access Layer Unit |`avc_sps` `avc_pps` `avc_sei`| -|`avc_pps` |H.264/AVC Picture Parameter Set || -|[`avc_sei`](#avc_sei) |H.264/AVC Supplemental Enhancement Information || -|`avc_sps` |H.264/AVC Sequence Parameter Set || -|[`avi`](#avi) |Audio Video Interleaved |`avc_au` `hevc_au` `mp3_frame` `flac_frame`| -|[`avro_ocf`](#avro_ocf) |Avro object container file || -|[`bencode`](#bencode) |BitTorrent bencoding || -|`bitcoin_blkdat` |Bitcoin blk.dat |`bitcoin_block`| -|[`bitcoin_block`](#bitcoin_block) |Bitcoin block |`bitcoin_transaction`| -|`bitcoin_script` |Bitcoin script || -|`bitcoin_transaction` |Bitcoin transaction |`bitcoin_script`| -|[`bits`](#bits) |Raw bits || -|[`bplist`](#bplist) |Apple Binary Property List || -|`bsd_loopback_frame` |BSD loopback frame |`inet_packet`| -|[`bson`](#bson) |Binary JSON || -|[`bytes`](#bytes) |Raw bytes || -|`bzip2` |bzip2 compression |`probe`| -|[`caff`](#caff) |Live2D Cubism archive |`probe`| -|[`cbor`](#cbor) |Concise Binary Object Representation || -|[`csv`](#csv) |Comma separated values || -|`dns` |DNS packet || -|`dns_tcp` |DNS packet (TCP) || -|`elf` |Executable and Linkable Format || -|`ether8023_frame` |Ethernet 802.3 frame |`inet_packet`| -|`exif` |Exchangeable Image File Format || -|`fairplay_spc` |FairPlay Server Playback Context || -|[`fit`](#fit) |Garmin Flexible and Interoperable Data Transfer || -|`flac` |Free Lossless Audio Codec file |`flac_metadatablocks` `flac_frame`| -|[`flac_frame`](#flac_frame) |FLAC frame || -|`flac_metadatablock` |FLAC metadatablock |`flac_streaminfo` `flac_picture` `vorbis_comment`| -|`flac_metadatablocks` |FLAC metadatablocks |`flac_metadatablock`| -|`flac_picture` |FLAC metadatablock picture |`image`| -|`flac_streaminfo` |FLAC streaminfo || -|`gif` |Graphics Interchange Format || -|`gzip` |gzip compression |`probe`| -|[`heif`](#heif) |High Efficiency Image Format |`av1_ccr` `av1_frame` `hevc_au` `hevc_dcr` `icc_profile`| -|`hevc_annexb` |H.265/HEVC Annex B |`hevc_nalu`| -|[`hevc_au`](#hevc_au) |H.265/HEVC Access Unit |`hevc_nalu`| -|`hevc_dcr` |H.265/HEVC Decoder Configuration Record |`hevc_nalu`| -|`hevc_nalu` |H.265/HEVC Network Access Layer Unit |`hevc_vps` `hevc_pps` `hevc_sps`| -|`hevc_pps` |H.265/HEVC Picture Parameter Set || -|`hevc_sps` |H.265/HEVC Sequence Parameter Set || -|`hevc_vps` |H.265/HEVC Video Parameter Set || -|[`html`](#html) |HyperText Markup Language || -|`icc_profile` |International Color Consortium profile || -|`icmp` |Internet Control Message Protocol || -|`icmpv6` |Internet Control Message Protocol v6 || -|`id3v1` |ID3v1 metadata || -|`id3v11` |ID3v1.1 metadata || -|`id3v2` |ID3v2 metadata |`image`| -|`ipv4_packet` |Internet protocol v4 packet |`ip_packet`| -|`ipv6_packet` |Internet protocol v6 packet |`ip_packet`| -|`jp2c` |JPEG 2000 codestream || -|`jpeg` |Joint Photographic Experts Group file |`exif` `icc_profile`| -|`json` |JavaScript Object Notation || -|`jsonl` |JavaScript Object Notation Lines || -|[`leveldb_descriptor`](#leveldb_descriptor) |LevelDB Descriptor || -|[`leveldb_log`](#leveldb_log) |LevelDB Log || -|[`leveldb_table`](#leveldb_table) |LevelDB Table || -|[`luajit`](#luajit) |LuaJIT 2.0 bytecode || -|[`macho`](#macho) |Mach-O macOS executable || -|`macho_fat` |Fat Mach-O macOS executable (multi-architecture) |`macho`| -|[`markdown`](#markdown) |Markdown || -|[`matroska`](#matroska) |Matroska file |`aac_frame` `av1_ccr` `av1_frame` `avc_au` `avc_dcr` `flac_frame` `flac_metadatablocks` `hevc_au` `hevc_dcr` `image` `mp3_frame` `mpeg_asc` `mpeg_pes_packet` `mpeg_spu` `opus_packet` `vorbis_packet` `vp8_frame` `vp9_cfm` `vp9_frame`| -|[`midi`](#midi) |Standard MIDI file || -|[`moc3`](#moc3) |MOC3 file || -|[`mp3`](#mp3) |MP3 file |`id3v2` `id3v1` `id3v11` `apev2` `mp3_frame`| -|`mp3_frame` |MPEG audio layer 3 frame |`mp3_frame_tags`| -|`mp3_frame_vbri` |MP3 frame Fraunhofer encoder variable bitrate tag || -|`mp3_frame_xing` |MP3 frame Xing/Info tag || -|[`mp4`](#mp4) |ISOBMFF, QuickTime and similar |`aac_frame` `av1_ccr` `av1_frame` `avc_au` `avc_dcr` `flac_frame` `flac_metadatablocks` `hevc_au` `hevc_dcr` `icc_profile` `id3v2` `image` `jp2c` `jpeg` `mp3_frame` `mpeg_es` `mpeg_pes_packet` `opus_packet` `png` `prores_frame` `protobuf_widevine` `pssh_playready` `vorbis_packet` `vp9_frame` `vpx_ccr`| -|`mpeg_asc` |MPEG-4 Audio Specific Config || -|`mpeg_es` |MPEG Elementary Stream |`mpeg_asc` `vorbis_packet`| -|`mpeg_pes` |MPEG Packetized elementary stream |`mpeg_pes_packet` `mpeg_spu`| -|`mpeg_pes_packet` |MPEG Packetized elementary stream packet || -|`mpeg_spu` |Sub Picture Unit (DVD subtitle) || -|`mpeg_ts` |MPEG Transport Stream || -|[`msgpack`](#msgpack) |MessagePack || -|[`negentropy`](#negentropy) |Negentropy message || -|[`nes`](#nes) |iNES/NES 2.0 cartridge ROM format || -|`ogg` |OGG file |`ogg_page` `vorbis_packet` `opus_packet` `flac_metadatablock` `flac_frame`| -|`ogg_page` |OGG page || -|[`opentimestamps`](#opentimestamps) |OpenTimestamps file || -|`opus_packet` |Opus packet |`vorbis_comment`| -|[`pcap`](#pcap) |PCAP packet capture |`link_frame` `tcp_stream` `ipv4_packet`| -|`pcapng` |PCAPNG packet capture |`link_frame` `tcp_stream` `ipv4_packet`| -|[`pg_btree`](#pg_btree) |PostgreSQL btree index file || -|[`pg_control`](#pg_control) |PostgreSQL control file || -|[`pg_heap`](#pg_heap) |PostgreSQL heap file || -|`png` |Portable Network Graphics file |`icc_profile` `exif`| -|`prores_frame` |Apple ProRes frame || -|[`protobuf`](#protobuf) |Protobuf || -|`protobuf_widevine` |Widevine protobuf |`protobuf`| -|`pssh_playready` |PlayReady PSSH || -|[`rtmp`](#rtmp) |Real-Time Messaging Protocol |`amf0` `mpeg_asc`| -|`safetensors` |SafeTensors |`json`| -|`sll2_packet` |Linux cooked capture encapsulation v2 |`inet_packet`| -|`sll_packet` |Linux cooked capture encapsulation |`inet_packet`| -|[`stl`](#stl) |Stereolithography || -|[`tap`](#tap) |TAP tape format for ZX Spectrum computers || -|`tar` |Tar archive |`probe`| -|`tcp_segment` |Transmission control protocol segment || -|`tiff` |Tag Image File Format |`icc_profile`| -|[`tls`](#tls) |Transport layer security |`asn1_ber`| -|`toml` |Tom's Obvious, Minimal Language || -|[`tzif`](#tzif) |Time Zone Information Format || -|[`tzx`](#tzx) |TZX tape format for ZX Spectrum computers |`tap`| -|`udp_datagram` |User datagram protocol |`udp_payload`| -|`vorbis_comment` |Vorbis comment |`flac_picture`| -|`vorbis_packet` |Vorbis packet |`vorbis_comment`| -|`vp8_frame` |VP8 frame || -|`vp9_cfm` |VP9 Codec Feature Metadata || -|`vp9_frame` |VP9 frame || -|`vpx_ccr` |VPX Codec Configuration Record || -|[`wasm`](#wasm) |WebAssembly Binary Format || -|`wav` |WAV file |`id3v2` `id3v1` `id3v11`| -|`webp` |WebP image |`exif` `vp8_frame` `icc_profile` `xml`| -|[`xml`](#xml) |Extensible Markup Language || -|`yaml` |YAML Ain't Markup Language || -|[`zip`](#zip) |ZIP archive |`probe`| -|`image` |Group |`gif` `heif` `jp2c` `jpeg` `mp4` `png` `tiff` `webp`| -|`inet_packet` |Group |`ipv4_packet` `ipv6_packet`| -|`ip_packet` |Group |`icmp` `icmpv6` `tcp_segment` `udp_datagram`| -|`link_frame` |Group |`bsd_loopback_frame` `ether8023_frame` `ipv4_packet` `ipv6_packet` `sll2_packet` `sll_packet`| -|`mp3_frame_tags` |Group |`mp3_frame_vbri` `mp3_frame_xing`| -|`probe` |Group |`adts` `aiff` `apple_bookmark` `ar` `avi` `avro_ocf` `bitcoin_blkdat` `bplist` `bzip2` `caff` `elf` `fit` `flac` `gif` `gzip` `heif` `html` `jp2c` `jpeg` `json` `jsonl` `leveldb_table` `luajit` `macho` `macho_fat` `matroska` `midi` `moc3` `mp3` `mp4` `mpeg_ts` `nes` `ogg` `opentimestamps` `pcap` `pcapng` `png` `tar` `tiff` `toml` `tzif` `tzx` `wasm` `wav` `webp` `xml` `yaml` `zip`| -|`tcp_stream` |Group |`dns_tcp` `rtmp` `tls`| -|`udp_payload` |Group |`dns`| - -[#]: sh-end - -## Global format options - -Currently the only global option is `force` and is used to ignore some format assertion errors. It can be used as a decode option or as a CLI `-o` option: - -``` -fq -d mp4 -o force=true file.mp4 -fq -d bytes 'mp4({force: true})' file.mp4 -``` - -## Format details - -[fq -rn -L . 'include "formats"; formats_sections']: sh-start - -## aac_frame -Advanced Audio Coding frame. - -### Options - -|Name |Default|Description| -|- |- |-| -|`object_type`|1 |Audio object type| - -### Examples - -Decode file using aac_frame options -``` -$ fq -d aac_frame -o object_type=1 . file -``` - -Decode value as aac_frame -``` -... | aac_frame({object_type:1}) -``` - -## apple_bookmark -Apple BookmarkData. - -Apple's `bookmarkData` format is used to encode information that can be resolved -into a `URL` object for a file even if the user moves or renames it. Can also -contain security scoping information for App Sandbox support. - -These `bookmarkData` blobs are often found endcoded in data fields of Binary -Property Lists. Notable examples include: - -- `com.apple.finder.plist` - contains an `FXRecentFolders` value, which is an - array of ten objects, each of which consists of a `name` and `file-bookmark` - field, which is a `bookmarkData` object for each recently accessed folder - location. - -- `com.apple.LSSharedFileList.RecentApplications.sfl2` - `sfl2` files are - actually `plist` files of the `NSKeyedArchiver` format. They can be parsed the - same as `plist` files, but they have a more complicated tree-like structure - than would typically be found, which can make locating and retrieving specific - values difficult, even once it has been converted to a JSON representation. - For more information about these types of files, see Sarah Edwards' excellent - research on the subject (link in references). - -`fq`'s `grep_by` function can be used to recursively descend through the decoded -tree, probing for and selecting any `bookmark` blobs, then converting them to -readable JSON with `torepr`: -``` -fq 'grep_by(.type=="data" and .value[0:4] == "book") | .value | apple_bookmark | -torepr' -``` - -### Authors -- David McDonald -[@dgmcdona](https://github.com/dgmcdona) -[@river_rat_504](https://twitter.com/river_rat_504) - -### References -- https://developer.apple.com/documentation/foundation/url/2143023-bookmarkdata -- https://mac-alias.readthedocs.io/en/latest/bookmark_fmt.html -- https://www.mac4n6.com/blog/2016/1/1/manual-analysis-of-nskeyedarchiver-formatted-plist-files-a-review-of-the-new-os-x-1011-recent-items -- https://michaellynn.github.io/2015/10/24/apples-bookmarkdata-exposed/ - -## asn1_ber -ASN1 BER (basic encoding rules, also CER and DER). - -Supports decoding BER, CER and DER (X.690). - -- Currently no extra validation is done for CER and DER. -- Does not support specifying a schema. -- Supports `torepr` but without schema all sequences and sets will be arrays. - -### Can be used to decode certificates etc - -```sh -$ fq -d bytes 'from_pem | asn1_ber | d' cert.pem -``` - -### Can decode nested values - -```sh -$ fq -d asn1_ber '.constructed[1].value | asn1_ber' file.ber -``` - -### Manual schema - -```sh -$ fq -d asn1_ber 'torepr as $r | ["version", "modulus", "private_exponent", "private_exponen", "prime1", "prime2", "exponent1", "exponent2", "coefficient"] | with_entries({key: .value, value: $r[.key]})' pkcs1.der -``` - -### References -- https://www.itu.int/ITU-T/studygroups/com10/languages/X.690_1297.pdf -- https://en.wikipedia.org/wiki/X.690 -- https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ -- https://lapo.it/asn1js/ - -## avc_au -H.264/AVC Access Unit. - -### Options - -|Name |Default|Description| -|- |- |-| -|`bottom_field_pic_order_in_frame_present_flag`|false || -|`cpb_cnt` |0 || -|`cpb_removal_delay_length` |0 || -|`delta_pic_order_always_zero_flag` |false || -|`dpb_output_delay_length` |0 || -|`frame_mbs_only_flag` |true || -|`initial_cpb_removal_delay_length` |0 || -|`length_size` |0 |Length value size| -|`log2max_frame_num` |4 || -|`log2max_pic_order_cnt_lsb` |4 || -|`nal_hrd_parameters_present` |false || -|`pic_order_cnt_type` |0 || -|`redundant_pic_cnt_present_flag` |false || -|`separate_colour_plane_flag` |false || -|`time_offset_length` |0 || -|`vcl_hrd_parameters_present` |false || - -### Examples - -Decode file using avc_au options -``` -$ fq -d avc_au -o bottom_field_pic_order_in_frame_present_flag=false -o cpb_cnt=0 -o cpb_removal_delay_length=0 -o delta_pic_order_always_zero_flag=false -o dpb_output_delay_length=0 -o frame_mbs_only_flag=true -o initial_cpb_removal_delay_length=0 -o length_size=0 -o log2max_frame_num=4 -o log2max_pic_order_cnt_lsb=4 -o nal_hrd_parameters_present=false -o pic_order_cnt_type=0 -o redundant_pic_cnt_present_flag=false -o separate_colour_plane_flag=false -o time_offset_length=0 -o vcl_hrd_parameters_present=false . file -``` - -Decode value as avc_au -``` -... | avc_au({bottom_field_pic_order_in_frame_present_flag:false,cpb_cnt:0,cpb_removal_delay_length:0,delta_pic_order_always_zero_flag:false,dpb_output_delay_length:0,frame_mbs_only_flag:true,initial_cpb_removal_delay_length:0,length_size:0,log2max_frame_num:4,log2max_pic_order_cnt_lsb:4,nal_hrd_parameters_present:false,pic_order_cnt_type:0,redundant_pic_cnt_present_flag:false,separate_colour_plane_flag:false,time_offset_length:0,vcl_hrd_parameters_present:false}) -``` - -## avc_nalu -H.264/AVC Network Access Layer Unit. - -### Options - -|Name |Default|Description| -|- |- |-| -|`bottom_field_pic_order_in_frame_present_flag`|false || -|`cpb_cnt` |0 || -|`cpb_removal_delay_length` |0 || -|`delta_pic_order_always_zero_flag` |false || -|`dpb_output_delay_length` |0 || -|`frame_mbs_only_flag` |true || -|`initial_cpb_removal_delay_length` |0 || -|`log2max_frame_num` |4 || -|`log2max_pic_order_cnt_lsb` |4 || -|`nal_hrd_parameters_present` |false || -|`pic_order_cnt_type` |0 || -|`redundant_pic_cnt_present_flag` |false || -|`separate_colour_plane_flag` |false || -|`time_offset_length` |0 || -|`vcl_hrd_parameters_present` |false || - -### Examples - -Decode file using avc_nalu options -``` -$ fq -d avc_nalu -o bottom_field_pic_order_in_frame_present_flag=false -o cpb_cnt=0 -o cpb_removal_delay_length=0 -o delta_pic_order_always_zero_flag=false -o dpb_output_delay_length=0 -o frame_mbs_only_flag=true -o initial_cpb_removal_delay_length=0 -o log2max_frame_num=4 -o log2max_pic_order_cnt_lsb=4 -o nal_hrd_parameters_present=false -o pic_order_cnt_type=0 -o redundant_pic_cnt_present_flag=false -o separate_colour_plane_flag=false -o time_offset_length=0 -o vcl_hrd_parameters_present=false . file -``` - -Decode value as avc_nalu -``` -... | avc_nalu({bottom_field_pic_order_in_frame_present_flag:false,cpb_cnt:0,cpb_removal_delay_length:0,delta_pic_order_always_zero_flag:false,dpb_output_delay_length:0,frame_mbs_only_flag:true,initial_cpb_removal_delay_length:0,log2max_frame_num:4,log2max_pic_order_cnt_lsb:4,nal_hrd_parameters_present:false,pic_order_cnt_type:0,redundant_pic_cnt_present_flag:false,separate_colour_plane_flag:false,time_offset_length:0,vcl_hrd_parameters_present:false}) -``` - -## avc_sei -H.264/AVC Supplemental Enhancement Information. - -### Options - -|Name |Default|Description| -|- |- |-| -|`cpb_cnt` |0 || -|`cpb_removal_delay_length` |0 || -|`delta_pic_order_always_zero_flag`|false || -|`dpb_output_delay_length` |0 || -|`frame_mbs_only_flag` |true || -|`initial_cpb_removal_delay_length`|0 || -|`log2max_frame_num` |4 || -|`log2max_pic_order_cnt_lsb` |4 || -|`nal_hrd_parameters_present` |false || -|`pic_order_cnt_type` |0 || -|`separate_colour_plane_flag` |false || -|`time_offset_length` |0 || -|`vcl_hrd_parameters_present` |false || - -### Examples - -Decode file using avc_sei options -``` -$ fq -d avc_sei -o cpb_cnt=0 -o cpb_removal_delay_length=0 -o delta_pic_order_always_zero_flag=false -o dpb_output_delay_length=0 -o frame_mbs_only_flag=true -o initial_cpb_removal_delay_length=0 -o log2max_frame_num=4 -o log2max_pic_order_cnt_lsb=4 -o nal_hrd_parameters_present=false -o pic_order_cnt_type=0 -o separate_colour_plane_flag=false -o time_offset_length=0 -o vcl_hrd_parameters_present=false . file -``` - -Decode value as avc_sei -``` -... | avc_sei({cpb_cnt:0,cpb_removal_delay_length:0,delta_pic_order_always_zero_flag:false,dpb_output_delay_length:0,frame_mbs_only_flag:true,initial_cpb_removal_delay_length:0,log2max_frame_num:4,log2max_pic_order_cnt_lsb:4,nal_hrd_parameters_present:false,pic_order_cnt_type:0,separate_colour_plane_flag:false,time_offset_length:0,vcl_hrd_parameters_present:false}) -``` - -## avi -Audio Video Interleaved. - -### Options - -|Name |Default|Description| -|- |- |-| -|`decode_extended_chunks`|true |Decode extended chunks| -|`decode_samples` |true |Decode samples| - -### Examples - -Decode file using avi options -``` -$ fq -d avi -o decode_extended_chunks=true -o decode_samples=true . file -``` - -Decode value as avi -``` -... | avi({decode_extended_chunks:true,decode_samples:true}) -``` - -### Samples - -AVI has many redundant ways to index samples so currently `.streams[].samples` will only include samples the most "modern" way used in the file. That is in order of stream super index, movi ix index then idx1 index. - -### Extract samples for stream 1 - -```sh -$ fq '.streams[1].samples[] | tobytes' file.avi > stream01.mp3 -``` - -### Show stream summary -```sh -$ fq -o decode_samples=false '[.chunks[0] | grep_by(.id=="LIST" and .type=="strl") | grep_by(.id=="strh") as {$type} | grep_by(.id=="strf") as {$format_tag, $compression} | {$type,$format_tag,$compression}]' *.avi -``` - -### Speed up decoding by disabling sample and extended chunks decoding - -If your not interested in sample details or extended chunks you can speed up decoding by using: -```sh -$ fq -o decode_samples=false -o decode_extended_chunks=false d file.avi -``` - -### References - -- [AVI RIFF File Reference](https://learn.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference) -- [OpenDML AVI File Format Extensions](http://www.jmcgowan.com/odmlff2.pdf) - -## avro_ocf -Avro object container file. - -Supports reading Avro Object Container Format (OCF) files based on the 1.11.0 specification. - -Capable of handling null, deflate, and snappy codecs for data compression. - -Limitations: - -- Schema does not support self-referential types, only built-in types. -- Decimal logical types are not supported for decoding, will just be treated as their primitive type - -### References -- https://avro.apache.org/docs/current/spec.html#Object+Container+Files - -### Authors -- Xentripetal -xentripetal@fastmail.com -[@xentripetal](https://github.com/xentripetal) - -## bencode -BitTorrent bencoding. - -### Convert represented value to JSON - -``` -$ fq -d bencode torepr file.torrent -``` - -### References -- https://wiki.theory.org/BitTorrentSpecification#Bencoding - -## bitcoin_block -Bitcoin block. - -### Options - -|Name |Default|Description| -|- |- |-| -|`has_header`|false |Has blkdat header| - -### Examples - -Decode file using bitcoin_block options -``` -$ fq -d bitcoin_block -o has_header=false . file -``` - -Decode value as bitcoin_block -``` -... | bitcoin_block({has_header:false}) -``` - -## bits -Raw bits. - -Decode to a slice and indexable binary of bits. - -### Slice and decode bit range - -```sh -$ echo 'some {"a":1} json' | fq -d bits '.[40:-48] | fromjson' -{ - "a": 1 -} -``` - -## Index bits - -```sh -✗ echo 'hello' | fq -d bits '.[4]' -1 -$ echo 'hello' | fq -c -d bits '[.[range(8)]]' -[0,1,1,0,1,0,0,0] -``` - -## bplist -Apple Binary Property List. - -### Show full decoding -```sh -$ fq d Info.plist -``` - -### Timestamps -Timestamps in Apple Binary Property Lists are encoded as Cocoa Core Data -timestamps, where the raw value is the floating point number of seconds since -January 1, 2001. By default, `fq` will render the raw floating point value. In -order to get the raw value or string description, use the `todescription` -function, you can use the `tovalue` and `todescription` functions: - -```sh -$ fq 'torepr.SomeTimeStamp | tovalue' Info.plist -685135328 - -$ fq 'torepr.SomeTimeStamp | todescription' Info.plist -"2022-09-17T19:22:08Z" -``` - - -### Get JSON representation - -`bplist` files can be converted to a JSON representation using the `torepr` filter: -```sh -$ fq torepr com.apple.UIAutomation.plist -{ - "UIAutomationEnabled": true -} -``` - -### Decoding NSKeyedArchiver serialized objects - -A common way that Swift and Objective-C libraries on macOS serialize objects -is through the NSKeyedArchiver API, which flattens objects into a list of elements -and class descriptions that are reconstructed into an object graph using CFUID -elements in the property list. `fq` includes a function, `from_ns_keyed_archiver`, -which will rebuild this object graph into a friendly representation. - -If no parameters are supplied, it will assume that there is a CFUID located at -`."$top".root` that specifies the root from which decoding should occur. If this -is not present, an error will be produced, asking the user to specify a root -object in the `.$objects` list from which to decode. - -The following examples show how this might be used (in this case, within the `fq` REPL): -``` -# Assume $top.root is present -bplist> from_ns_keyed_archiver - -# Specify optional root -bplist> from_ns_keyed_archiver(1) -``` - -### Authors -- David McDonald -[@dgmcdona](https://github.com/dgmcdona) - -### References -- http://fileformats.archiveteam.org/wiki/Property_List/Binary -- https://medium.com/@karaiskc/understanding-apples-binary-property-list-format-281e6da00dbd -- https://opensource.apple.com/source/CF/CF-550/CFBinaryPList.c - -## bson -Binary JSON. - -### Limitations - -- The decimal128 type is not supported for decoding, will just be treated as binary - -### Convert represented value to JSON - -``` -$ fq -d bson torepr file.bson -``` - -### Filter represented value - -``` -$ fq -d bson 'torepr | select(.name=="bob")' file.bson -``` - -### Authors -- Mattias Wadman mattias.wadman@gmail.com, original author -- Matt Dale [@matthewdale](https://github.com/matthewdale), additional types and bug fixes - -### References -- https://bsonspec.org/spec.html - -## bytes -Raw bytes. - -Decode to a slice and indexable binary of bytes. - -### Slice out byte ranges - -```sh -$ echo -n 'hello' | fq -d bytes '.[-3:]' > last_3_bytes -$ echo -n 'hello' | fq -d bytes '[.[-2:], .[0:2]] | tobytes' > first_last_2_bytes_swapped -``` - -### Slice and decode byte range - -```sh -$ echo 'some {"a":1} json' | fq -d bytes '.[5:-6] | fromjson' -{ - "a": 1 -} -``` - -## Index bytes - -```sh -$ echo 'hello' | fq -d bytes '.[1]' -101 -``` - -## caff -Live2D Cubism archive. - -### Options - -|Name |Default|Description| -|- |- |-| -|`uncompress`|true |Uncompress and probe files| - -### Examples - -Decode file using caff options -``` -$ fq -d caff -o uncompress=true . file -``` - -Decode value as caff -``` -... | caff({uncompress:true}) -``` - -### Authors -- [@ronsor](https://github.com/ronsor) - -## cbor -Concise Binary Object Representation. - -### Convert represented value to JSON - -``` -$ fq -d cbor torepr file.cbor -``` - -### References -- https://en.wikipedia.org/wiki/CBOR -- https://www.rfc-editor.org/rfc/rfc8949.html - -## csv -Comma separated values. - -### Options - -|Name |Default|Description| -|- |- |-| -|`comma` |, |Separator character| -|`comment`|# |Comment line character| - -### Examples - -Decode file using csv options -``` -$ fq -d csv -o comma="," -o comment="#" . file -``` - -Decode value as csv -``` -... | csv({comma:",",comment:"#"}) -``` - -### TSV to CSV - -```sh -$ fq -d csv -o comma="\t" to_csv file.tsv -``` - -### Convert rows to objects based on header row - -```sh -$ fq -d csv '.[0] as $t | .[1:] | map(with_entries(.key = $t[.key]))' file.csv -``` - -## fit -Garmin Flexible and Interoperable Data Transfer. - -### Limitations - -- Fields with subcomponents, such as "compressed_speed_distance" field on globalMessageNumber 20 is not represented correctly. - The field is read as 3 separate bytes where the first 12 bits are speed and the last 12 bits are distance. -- There are still lots of UNKOWN fields due to gaps in Garmins SDK Profile documentation. (Currently FIT SDK 21.126) -- Compressed timestamp messages are not accumulated against last known full timestamp. - -### Convert stream of data messages to JSON array - -``` -$ fq '[.data_records[] | select(.record_header.message_type == "data").data_message]' file.fit -``` - -### Authors -- Mikael Lofjärd mikael.lofjard@gmail.com, original author - -### References -- https://developer.garmin.com/fit/protocol/ -- https://developer.garmin.com/fit/cookbook/decoding-activity-files/ - -## flac_frame -FLAC frame. - -### Options - -|Name |Default|Description| -|- |- |-| -|`bits_per_sample`|16 |Bits per sample| -|`sample_details` |false |Decode more sample details like residuals etc| - -### Examples - -Decode file using flac_frame options -``` -$ fq -d flac_frame -o bits_per_sample=16 -o sample_details=false . file -``` - -Decode value as flac_frame -``` -... | flac_frame({bits_per_sample:16,sample_details:false}) -``` - -## heif -High Efficiency Image Format. - -### Options - -|Name |Default|Description| -|- |- |-| -|`allow_truncated`|false |Allow box to be truncated| - -### Examples - -Decode file using heif options -``` -$ fq -d heif -o allow_truncated=false . file -``` - -Decode value as heif -``` -... | heif({allow_truncated:false}) -``` - -## hevc_au -H.265/HEVC Access Unit. - -### Options - -|Name |Default|Description| -|- |- |-| -|`length_size`|4 |Length value size| - -### Examples - -Decode file using hevc_au options -``` -$ fq -d hevc_au -o length_size=4 . file -``` - -Decode value as hevc_au -``` -... | hevc_au({length_size:4}) -``` - -## html -HyperText Markup Language. - -### Options - -|Name |Default|Description| -|- |- |-| -|`array` |false |Decode as nested arrays| -|`attribute_prefix`|@ |Prefix for attribute keys| -|`seq` |false |Use seq attribute to preserve element order| - -### Examples - -Decode file using html options -``` -$ fq -d html -o array=false -o attribute_prefix="@" -o seq=false . file -``` - -Decode value as html -``` -... | html({array:false,attribute_prefix:"@",seq:false}) -``` - -HTML is decoded in HTML5 mode and will always include ``, `` and `` element. - -See xml format for more examples and how to preserve element order and how to encode to xml. - -There is no `to_html` function, see `to_xml` instead. - -### Element as object - -```sh -# decode as object is the default -$ echo 'text' | fq -d html -{ - "html": { - "body": { - "a": { - "#text": "text", - "@href": "url" - } - }, - "head": "" - } -} -``` - -### Element as array - -```sh -$ echo 'text' | fq -d html -o array=true -[ - "html", - null, - [ - [ - "head", - null, - [] - ], - [ - "body", - null, - [ - [ - "a", - { - "#text": "text", - "href": "url" - }, - [] - ] - ] - ] - ] -] - -# decode html files to a {file: "title", ...} object -$ fq -n -d html '[inputs | {key: input_filename, value: .html.head.title?}] | from_entries' *.html - -# href:s in file -$ fq -r -o array=true -d html '.. | select(.[0] == "a" and .[1].href)?.[1].href' file.html -``` - -## leveldb_descriptor -LevelDB Descriptor. - -### Limitations - -- fragmented non-"full" records are not merged and decoded further. - -### Authors - -- [@mikez](https://github.com/mikez), original author - -### References - -- https://github.com/google/leveldb/blob/main/doc/impl.md#manifest -- https://github.com/google/leveldb/blob/main/doc/log_format.md -- https://github.com/google/leveldb/blob/main/db/version_edit.cc - -## leveldb_log -LevelDB Log. - -### Limitations - -- fragmented non-"full" records are not merged and decoded further. - -### Authors - -- [@mikez](https://github.com/mikez), original author - -### References - -- https://github.com/google/leveldb/blob/main/doc/impl.md#log-files -- https://github.com/google/leveldb/blob/main/doc/log_format.md -- https://github.com/google/leveldb/blob/main/db/write_batch.cc - -## leveldb_table -LevelDB Table. - -### Limitations - -- no Meta Blocks (like "filter") are decoded yet. -- Zstandard uncompression is not implemented yet. - -### Authors - -- [@mikez](https://github.com/mikez), original author - -### References - -- https://github.com/google/leveldb/blob/main/doc/table_format.md -- https://github.com/google/leveldb/blob/main/doc/impl.md -- https://github.com/google/leveldb/blob/main/doc/index.md - -## luajit -LuaJIT 2.0 bytecode. - -### Authors -- [@dlatchx](https://github.com/dlatchx) - -### References -- https://github.com/LuaJIT/LuaJIT/blob/v2.1/src/lj_bcdump.h -- http://scm.zoomquiet.top/data/20131216145900/index.html - -## macho -Mach-O macOS executable. - -Supports decoding vanilla and FAT Mach-O binaries. - -### Select 64bit load segments - -```sh -$ fq '.load_commands[] | select(.cmd=="segment_64")' file -``` - -### References -- https://github.com/aidansteele/osx-abi-macho-file-format-reference - -### Authors -- Sıddık AÇIL -acils@itu.edu.tr -[@Akaame](https://github.com/Akaame) - -## markdown -Markdown. - -### Array with all level 1 and 2 headers -```sh -$ fq -d markdown '[.. | select(.type=="heading" and .level<=2)?.children[0]]' file.md -``` -## matroska -Matroska file. - -### Options - -|Name |Default|Description| -|- |- |-| -|`decode_samples`|true |Decode samples| - -### Examples - -Decode file using matroska options -``` -$ fq -d matroska -o decode_samples=true . file -``` - -Decode value as matroska -``` -... | matroska({decode_samples:true}) -``` - -### Lookup element using path - -```sh -$ fq 'matroska_path(".Segment.Tracks[0)")' file.mkv -``` - -### Get path to element - -```sh -$ fq 'grep_by(.id == "Tracks") | matroska_path' file.mkv -``` - -### References -- https://www.rfc-editor.org/info/rfc8794 -- https://matroska.org/technical/specs/index.html -- https://www.matroska.org/technical/basics.html -- https://www.matroska.org/technical/codec_specs.html -- https://wiki.xiph.org/MatroskaOpus - -## midi -Standard MIDI file. - -### Notes - -1. Only supports the MIDI 1.0 MIDI file specification. -2. Only supports _MThd_ and _MTrk_ chunks. -3. Does only basic validation on the MIDI data. - -### Sample queries - -1. Extract the track names from a MIDI file -``` -fq -d midi -d midi '.. | select(.event=="track_name")? | "\(.track_name)"' midi/twinkle.mid -``` - -2. Extract the tempo changes from a MIDI file -``` -fq -d midi '.. | select(.event=="tempo")?.tempo' midi/twinkle.mid -``` - -3. Extract the key changes from a MIDI file -``` -fq -d midi '.. | select(.event=="key_signature")?.key_signature' midi/twinkle.mid -``` - -4. Extract NoteOn events: -``` -fq -d midi 'grep_by(.event=="note_on") | [.time.tick, .note_on.note] | join(" ")' midi/twinkle.mid -``` - -### Authors -- [transcriptaze](https://github.com/transcriptaze) - -### References - -1. [The Complete MIDI 1.0 Detailed Specification](https://www.midi.org/specifications/item/the-midi-1-0-specification) -2. [Standard MIDI Files](https://midi.org/standard-midi-files) -3. [Standard MIDI File (SMF) Format](http://midi.teragonaudio.com/tech/midifile.htm) -4. [MIDI Files Specification](http://www.somascape.org/midi/tech/mfile.html) -5. [MIDI SMPTE Offset meta message](https://www.recordingblogs.com/wiki/midi-smpte-offset-meta-message) -6. [Somascape MIDI Files Specification](http://www.somascape.org/midi/tech/mfile.html#meta) - -## moc3 -MOC3 file. - -### Authors -- [@ronsor](https://github.com/ronsor) - -## mp3 -MP3 file. - -### Options - -|Name |Default|Description| -|- |- |-| -|`max_sync_seek` |32768 |Max byte distance to next sync| -|`max_unique_header_configs`|5 |Max number of unique frame header configs allowed| -|`max_unknown` |50 |Max percent (0-100) unknown bits| - -### Examples - -Decode file using mp3 options -``` -$ fq -d mp3 -o max_sync_seek=32768 -o max_unique_header_configs=5 -o max_unknown=50 . file -``` - -Decode value as mp3 -``` -... | mp3({max_sync_seek:32768,max_unique_header_configs:5,max_unknown:50}) -``` - -## mp4 -ISOBMFF, QuickTime and similar. - -### Options - -|Name |Default|Description| -|- |- |-| -|`allow_truncated`|false |Allow box to be truncated| -|`decode_samples` |true |Decode track samples| -|`skip_samples` |false |Skip track samples| - -### Examples - -Decode file using mp4 options -``` -$ fq -d mp4 -o allow_truncated=false -o decode_samples=true -o skip_samples=false . file -``` - -Decode value as mp4 -``` -... | mp4({allow_truncated:false,decode_samples:true,skip_samples:false}) -``` - -### Speed up decoding by not decoding samples - -```sh -# manually decode first sample as a aac_frame -$ fq -o decode_samples=false '.tracks[0].samples[0] | aac_frame | d' file.mp4 -``` - -### Entries for first edit list as values - -```sh -$ fq 'first(grep_by(.type=="elst").entries) | tovalue' file.mp4 -``` - -### Whole box tree as JSON (exclude mdat data and tracks) - -```sh -$ fq 'del(.tracks) | grep_by(.type=="mdat").data = "" | tovalue' file.mp4 -``` - -### Force decode a single box - -```sh -$ fq -n '"AAAAHGVsc3QAAAAAAAAAAQAAADIAAAQAAAEAAA==" | from_base64 | mp4({force:true}) | d' -``` - -### Lookup mp4 box using a mp4 box path. - -```sh -# | mp4_path($path) -> -$ fq 'mp4_path(".moov.trak[1]")' file.mp4 -``` - -### Get mp4 box path for a decode value box. - -```sh -# | mp4_path -> string -$ fq 'grep_by(.type == "trak") | mp4_path' file.mp4 -``` - -### References - -- [ISO/IEC base media file format (MPEG-4 Part 12)](https://en.wikipedia.org/wiki/ISO/IEC_base_media_file_format) -- [Quicktime file format](https://developer.apple.com/standards/qtff-2001.pdf) - -## msgpack -MessagePack. - -### Convert represented value to JSON - -``` -$ fq -d msgpack torepr file.msgpack -``` - -### References -- https://github.com/msgpack/msgpack/blob/master/spec.md - -## negentropy -Negentropy message. - -### View a full Negentropy message - -``` -$ fq -d negentropy dd file -``` - -### Or from hex - -``` -$ echo '6186b7abb47c0001108e4206828ee3bf34258465809a337c6c00019a68e37b177a50b3ae7164ccc628b962020114019c1381281c9e3849d5fbd514b7bb65ad0101e601fbf7451f5d22e7fa36ae3e910e9f5215020157014a1b26853e06e9c32eb41b1df4f9ab300201e6011840e273c84bb1344f1d4e15d9aa67920200016f12ee2340888653f10b0ec2d438ac9f0101840156d2d796f4dff004ab369b9bcfa4d81e020187013f1b3c8a019800d5764e2de6bdfd2785020114017caaf0acb5dfe249aa0f7f742402168a01018301e7b8c4decb1eae455ca5714281e3245302017a01409c22636b097362df125ddffb6d944302015b01f332208bee82acf8ed922853ee54057f020001fc3e51fdb0b92966e38017f7959903850101cc01428ce0c96d49f15b50143e4fb228cb9300000131712d30e5296a7a45d07bba452d61cd' | fq -R 'from_hex | negentropy | dd' -``` - -### Check how many ranges the message has and how many of those are of 'fingerprint' mode - -``` -$ fq -d negentropy '.bounds | length as $total | map(select(.mode == "fingerprint")) | length | {$total, fingerprint: .}' message -``` - -### Check get all ids in all idlists - -``` -$ fq -d negentropy '.bounds | map(select(.mode == "idlist") | .idlist | .ids) | flatten' message -``` - -### Authors -- fiatjaf, https://fiatjaf.com - -### References -- https://github.com/hoytech/negentropy - -## nes -iNES/NES 2.0 cartridge ROM format. - -### Limitations - -- `prg_rom`, `chr_rom` and `trainer` fields may contain data that is just random - junk from the memory chips, since they are of a fixed size. -- The `nes_toasm` function outputs ALL opcodes, including the unofficial ones, - which means that none of the regular assemblers can recompile it. -- The `nes_tokitty` function works on tiles in `chr_rom` but only outputs a Kitty - graphics compatible string. You need to manually `printf` that string to get - Kitty (or another compatible terminal) to output the graphics. - -### Decompile PRG ROM -``` -$ fq -r '.prg_rom[] | nes_toasm' file.nes -``` - -### Print out first CHR ROM tile in Kitty (or Konsole, wayst, WezTerm) at size 5 -``` -$ printf $(fq -r -d nes '.chr_rom[0] | nes_tokitty(5)' file.nes) -``` - -### Print out all CHR ROM tiles in Kitty (with Bash) at size 5 -``` -$ for line in $(fq -r '.chr_rom[] | nes_tokitty(5)' file.nes);do printf "%b%s" "$line";done -``` - -### Authors -- Mikael Lofjärd mikael.lofjard@gmail.com, original author - -### References -- https://www.nesdev.org/wiki/INES -- https://www.nesdev.org/wiki/NES_2.0 -- https://www.nesdev.org/wiki/CPU -- https://bugzmanov.github.io/nes_ebook/chapter_6_3.html - -## opentimestamps -OpenTimestamps file. - -### View a full OpenTimestamps file - -``` -$ fq dd file.ots -``` - -### List the names of the Calendar servers used - -``` -$ fq '.operations | map(select(.attestation_type == "calendar") | .url)' file.ots -``` - -### Check if there are Bitcoin attestations present - -``` -$ fq '.operations | map(select(.attestation_type == "bitcoin")) | length > 0' file.ots -``` - -### Authors -- fiatjaf, https://fiatjaf.com - -### References -- https://opentimestamps.org/ -- https://github.com/opentimestamps/python-opentimestamps - -## pcap -PCAP packet capture. - -### Build object with number of (reassembled) TCP bytes sent to/from client IP -```sh -# for a pcapng file you would use .[0].tcp_connections for first section -$ fq '.tcp_connections | group_by(.client.ip) | map({key: .[0].client.ip, value: map(.client.stream, .server.stream | tobytes.size) | add}) | from_entries' -{ - "10.1.0.22": 15116, - "10.99.12.136": 234, - "10.99.12.150": 218 -} -``` -## pg_btree -PostgreSQL btree index file. - -### Options - -|Name |Default|Description| -|- |- |-| -|`page`|0 |First page number in file, default is 0| - -### Examples - -Decode file using pg_btree options -``` -$ fq -d pg_btree -o page=0 . file -``` - -Decode value as pg_btree -``` -... | pg_btree({page:0}) -``` - -### Btree index meta page - -```sh -$ fq -d pg_btree -o flavour=postgres14 ".[0] | d" 16404 -``` - -### Btree index page - -```sh -$ fq -d pg_btree -o flavour=postgres14 ".[1]" 16404 -``` - -### Authors -- Pavel Safonov -p.n.safonov@gmail.com -[@pnsafonov](https://github.com/pnsafonov) - -### References -- https://www.postgresql.org/docs/current/storage-page-layout.html -## pg_control -PostgreSQL control file. - -### Options - -|Name |Default|Description| -|- |- |-| -|`flavour`| |PostgreSQL flavour: postgres14, pgproee14.., postgres10| - -### Examples - -Decode file using pg_control options -``` -$ fq -d pg_control -o flavour="" . file -``` - -Decode value as pg_control -``` -... | pg_control({flavour:""}) -``` - -### Decode content of pg_control file - -```sh -$ fq -d pg_control -o flavour=postgres14 d pg_control -``` - -### Specific fields can be got by request - -```sh -$ fq -d pg_control -o flavour=postgres14 ".state, .check_point_copy.redo, .wal_level" pg_control -``` - -### Authors -- Pavel Safonov -p.n.safonov@gmail.com -[@pnsafonov](https://github.com/pnsafonov) - -### References -- https://github.com/postgres/postgres/blob/REL_14_2/src/include/catalog/pg_control.h -## pg_heap -PostgreSQL heap file. - -### Options - -|Name |Default |Description| -|- |- |-| -|`flavour`|postgres14|PostgreSQL flavour: postgres14, pgproee14.., postgres10| -|`page` |0 |First page number in file, default is 0| -|`segment`|0 |Segment file number (16790.1 is 1), default is 0| - -### Examples - -Decode file using pg_heap options -``` -$ fq -d pg_heap -o flavour="postgres14" -o page=0 -o segment=0 . file -``` - -Decode value as pg_heap -``` -... | pg_heap({flavour:"postgres14",page:0,segment:0}) -``` - -### To see heap page's content -```sh -$ fq -d pg_heap -o flavour=postgres14 ".[0]" 16994 -``` - -### To see page's header - -```sh -$ fq -d pg_heap -o flavour=postgres14 ".[0].page_header" 16994 -``` - -### First and last item pointers on first page - -```sh -$ fq -d pg_heap -o flavour=postgres14 ".[0].pd_linp[0, -1]" 16994 -``` - -### First and last tuple on first page - -```sh -$ fq -d pg_heap -o flavour=postgres14 ".[0].tuples[0, -1]" 16994 -``` - -### Authors -- Pavel Safonov -p.n.safonov@gmail.com -[@pnsafonov](https://github.com/pnsafonov) - -### References -- https://www.postgresql.org/docs/current/storage-page-layout.html -## protobuf -Protobuf. - -### Can decode sub messages - -```sh -$ fq -d protobuf '.fields[6].wire_value | protobuf | d' file -``` - -### References -- https://developers.google.com/protocol-buffers/docs/encoding - -## rtmp -Real-Time Messaging Protocol. - -Current only supports plain RTMP (not RTMPT or encrypted variants etc) with AMF0 (not AMF3). - -### Show rtmp streams in PCAP file -```sh -fq '.tcp_connections[] | select(.server.port=="rtmp") | d' file.cap -``` - -### References -- https://rtmp.veriskope.com/docs/spec/ -- https://rtmp.veriskope.com/pdf/video_file_format_spec_v10.pdf - -## stl -Stereolithography. - -Decode binary STL (Stereolithography, Standard Tesselation Language) files. - -### Current limitations - -* No support for ASCII STL files -* No support for VisCAM and SolidView colors -* No support for Materialise Magics colors - -## tap -TAP tape format for ZX Spectrum computers. - -The TAP- (and BLK-) format is nearly a direct copy of the data that is stored -in real tapes, as it is written by the ROM save routine of the ZX-Spectrum. -A TAP file is simply one data block or a group of 2 or more data blocks, one -followed after the other. The TAP file may be empty. - -You will often find this format embedded inside the TZX tape format. - -The default file extension is `.tap`. - -### Processing JSON files - -When needing to process a generated JSON file it's recommended to convert the -plain data bytes to an array by setting `bits_format=byte_array`: - -```bash -fq -o bits_format=byte_array -d tap -V d /path/to/file.tap -``` - -### Authors - -- Michael R. Cook work.mrc@pm.me, original author - -### References - -- https://worldofspectrum.net/zx-modules/fileformats/tapformat.html - -## tls -Transport layer security. - -### Options - -|Name |Default|Description| -|- |- |-| -|`keylog`| |NSS Key Log content| - -### Examples - -Decode file using tls options -``` -$ fq -d tls -o keylog="" . file -``` - -Decode value as tls -``` -... | tls({keylog:""}) -``` - -Supports decoding of most standard records, messages and extensions. Can also decrypt most standard cipher suits in a PCAP with traffic in both directions if a NSS key log is provided. - -### Decode and decrypt provding a PCAP and key log - -Write traffic to a PCAP file: - -```sh -$ tcpdump -i -w traffic.pcap -``` - -Make sure your curl TLS backend support `SSLKEYLOGFILE` and do: -```sh -$ SSLKEYLOGFILE=traffic.keylog curl --tls-max 1.2 https://host/path -``` - -Decode, decrypt and query. Uses `keylog=@` to read option value from keylog file: -```sh -# decode and show whole tree -$ fq -o keylog=@traffic.keylog d traffic.pcap - -# write unencrypted server response to a file. -# first .stream is the TCP stream, second .stream is TLS application data stream -# -# first TCP connections: -$ fq -o keylog=@traffic.keylog '.tcp_connections[0].server.stream.stream | tobytes' traffic.pcap > data -# first TLS connection: -$ fq -o keylog=@traffic.keylog 'first(grep_by(.server.stream | format == "tls")).server.stream.stream | tobytes' > data -``` - -### Supported cipher suites for decryption - -`TLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA`, -`TLS_DH_ANON_EXPORT_WITH_RC4_40_MD5`, -`TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA`, -`TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA`, -`TLS_DHE_DSS_WITH_AES_128_CBC_SHA`, -`TLS_DHE_DSS_WITH_AES_128_CBC_SHA256`, -`TLS_DHE_DSS_WITH_AES_128_GCM_SHA256`, -`TLS_DHE_DSS_WITH_AES_256_CBC_SHA`, -`TLS_DHE_DSS_WITH_AES_256_CBC_SHA256`, -`TLS_DHE_DSS_WITH_AES_256_GCM_SHA384`, -`TLS_DHE_DSS_WITH_DES_CBC_SHA`, -`TLS_DHE_DSS_WITH_RC4_128_SHA`, -`TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA`, -`TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_DHE_RSA_WITH_AES_128_CBC_SHA`, -`TLS_DHE_RSA_WITH_AES_128_CBC_SHA256`, -`TLS_DHE_RSA_WITH_AES_128_GCM_SHA256`, -`TLS_DHE_RSA_WITH_AES_256_CBC_SHA`, -`TLS_DHE_RSA_WITH_AES_256_CBC_SHA256`, -`TLS_DHE_RSA_WITH_AES_256_GCM_SHA384`, -`TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256`, -`TLS_DHE_RSA_WITH_DES_CBC_SHA`, -`TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA`, -`TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256`, -`TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256`, -`TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA`, -`TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384`, -`TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384`, -`TLS_ECDH_ECDSA_WITH_RC4_128_SHA`, -`TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_ECDH_RSA_WITH_AES_128_CBC_SHA`, -`TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256`, -`TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256`, -`TLS_ECDH_RSA_WITH_AES_256_CBC_SHA`, -`TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384`, -`TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384`, -`TLS_ECDH_RSA_WITH_RC4_128_SHA`, -`TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA`, -`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA`, -`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256`, -`TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`, -`TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA`, -`TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA`, -`TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384`, -`TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384eadAESGCM`, -`TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256`, -`TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305`, -`TLS_ECDHE_ECDSA_WITH_RC4_128_SHA`, -`TLS_ECDHE_ECDSA_WITH_RC4_128_SHA`, -`TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA`, -`TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256`, -`TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA`, -`TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA`, -`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA`, -`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256`, -`TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`, -`TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA`, -`TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA`, -`TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384`, -`TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`, -`TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256`, -`TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305`, -`TLS_ECDHE_RSA_WITH_RC4_128_SHA`, -`TLS_ECDHE_RSA_WITH_RC4_128_SHA`, -`TLS_PSK_WITH_AES_128_CBC_SHA`, -`TLS_PSK_WITH_AES_256_CBC_SHA`, -`TLS_PSK_WITH_RC4_128_SHA`, -`TLS_RSA_EXPORT_WITH_DES40_CBC_SHA`, -`TLS_RSA_EXPORT_WITH_RC4_40_MD5`, -`TLS_RSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_RSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_RSA_WITH_AES_128_CBC_SHA`, -`TLS_RSA_WITH_AES_128_CBC_SHA`, -`TLS_RSA_WITH_AES_128_CBC_SHA256`, -`TLS_RSA_WITH_AES_128_CBC_SHA256`, -`TLS_RSA_WITH_AES_128_GCM_SHA256`, -`TLS_RSA_WITH_AES_128_GCM_SHA256`, -`TLS_RSA_WITH_AES_256_CBC_SHA`, -`TLS_RSA_WITH_AES_256_CBC_SHA`, -`TLS_RSA_WITH_AES_256_CBC_SHA256`, -`TLS_RSA_WITH_AES_256_GCM_SHA384`, -`TLS_RSA_WITH_AES_256_GCM_SHA384`, -`TLS_RSA_WITH_DES_CBC_SHA`, -`TLS_RSA_WITH_RC4_128_MD5`, -`TLS_RSA_WITH_RC4_128_SHA`, -`TLS_RSA_WITH_RC4_128_SHA` - -### References - -- [RFC 5246: The Transport Layer Security (TLS) Protocol](https://www.rfc-editor.org/rfc/rfc5246) -- [RFC 6101: The Secure Sockets Layer (SSL) Protocol Version 3.0](https://www.rfc-editor.org/rfc/rfc) - -## tzif -Time Zone Information Format. - -### Get last transition time -```sh -fq '.v2plusdatablock.transition_times[-1] | tovalue' tziffile -``` - -### Count leap second records -```sh -fq '.v2plusdatablock.leap_second_records | length' tziffile -``` - -### Authors -- Takashi Oguma -[@bitbears-dev](https://github.com/bitbears-dev) -[@0xb17bea125](https://twitter.com/0xb17bea125) - -### References -- https://datatracker.ietf.org/doc/html/rfc8536 - -## tzx -TZX tape format for ZX Spectrum computers. - -`TZX` is a file format designed to preserve cassette tapes compatible with the -ZX Spectrum computers, although some specialized versions of the format have -been defined for other machines such as the Amstrad CPC and C64. - -The format was originally created by Tomaz Kac, who was maintainer until -`revision 1.13`, before passing it to Martijn v.d. Heide. For a brief period -the company Ramsoft became the maintainers, and created revision `v1.20`. - -The default file extension is `.tzx`. - -### Processing JSON files - -When needing to process a generated JSON file it's recommended to convert the -plain data bytes to an array by setting `bits_format=byte_array`: - -```bash -fq -o bits_format=byte_array -d tzx -V d /path/to/file.tzx -``` - -### Authors - -- Michael R. Cook work.mrc@pm.me, original author - -### References - -- https://worldofspectrum.net/TZXformat.html - -## wasm -WebAssembly Binary Format. - -### Count opcode usage -```sh -$ fq '.sections[] | select(.id == "code_section") | [.. | .opcode? // empty] | count | map({key: .[0], value: .[1]}) | from_entries' file.wasm -``` - -### List exports and imports -```sh -$ fq '.sections | {import: map(select(.id == "import_section").content.im.x[].nm.b), export: map(select(.id == "export_section").content.ex.x[].nm.b)}' file.wasm -``` - -### Authors -- Takashi Oguma -[@bitbears-dev](https://github.com/bitbears-dev) -[@0xb17bea125](https://twitter.com/0xb17bea125) - -### References -- https://webassembly.github.io/spec/core/ - -## xml -Extensible Markup Language. - -### Options - -|Name |Default|Description| -|- |- |-| -|`array` |false |Decode as nested arrays| -|`attribute_prefix`|@ |Prefix for attribute keys| -|`seq` |false |Use seq attribute to preserve element order| - -### Examples - -Decode file using xml options -``` -$ fq -d xml -o array=false -o attribute_prefix="@" -o seq=false . file -``` - -Decode value as xml -``` -... | xml({array:false,attribute_prefix:"@",seq:false}) -``` - -XML can be decoded and encoded into jq values in two ways, elements as object or array. -Which variant to use depends a bit what you want to do. The object variant might be easier -to query for a specific value but array might be easier to use to generate xml or to query -after all elements of some kind etc. - -Encoding is done using the `to_xml` function and it will figure what variant that is used based on the input value. -Is has two optional options `indent` and `attribute_prefix`. - -### Elements as object - -Element can have different shapes depending on body text, attributes and children: - -- `text` is `{"a":{"#text":"text","@key":"value"}}`, has text (`#text`) and attributes (`@key`) -- `text` is `{"a":"text"}` -- `text` is `{"a":{"b":"text"}}` one child with only text and no attributes -- `text` is `{"a":{"b":["","text"]}}` two children with same name end up in an array -- `text` is `{"a":{"b":["",{"#text":"text","@key":"value"}]}}` - -If there is `#seq` attribute it encodes the child element order. Use `-o seq=true` to include sequence number when decoding, -otherwise order might be lost. - -```sh -# decode as object is the default -$ echo 'bbbccc' | fq -d xml -o seq=true -{ - "a": { - "b": [ - { - "#seq": 0 - }, - { - "#seq": 1, - "#text": "bbb" - } - ], - "c": { - "#seq": 2, - "#text": "ccc", - "@attr": "value" - } - } -} - -# access text of the element -$ echo 'bbbccc' | fq '.a.c["#text"]' -"ccc" - -# decode to object and encode to xml -$ echo 'bbbccc' | fq -r -d xml -o seq=true 'to_xml({indent:2})' - - - bbb - ccc - -``` - -### Elements as array - -Elements are arrays of the shape `["#text": "body text", "attr_name", {key: "attr value"}|null, [, ...]]`. - -```sh -# decode as array -$ echo 'bbbccc' | fq -d xml -o array=true -[ - "a", - null, - [ - [ - "b", - null, - [] - ], - [ - "b", - { - "#text": "bbb" - }, - [] - ], - [ - "c", - { - "#text": "ccc", - "attr": "value" - }, - [] - ] - ] -] - -# decode to array and encode to xml -$ echo 'bbbccc' | fq -r -d xml -o array=true -o seq=true 'to_xml({indent:2})' - - - bbb - ccc - - -# access text of the element, the object variant above is probably easier to use -$ echo 'bbbccc' | fq -o array=true '.[2][2][1]["#text"]' -"ccc" -``` - -### References -- [xml.com's Converting Between XML and JSON](https://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html) - -## zip -ZIP archive. - -### Options - -|Name |Default|Description| -|- |- |-| -|`uncompress`|true |Uncompress and probe files| - -### Examples - -Decode file using zip options -``` -$ fq -d zip -o uncompress=true . file -``` - -Decode value as zip -``` -... | zip({uncompress:true}) -``` - -Supports ZIP64. - -## Timestamp and time zones - -The timestamp accessed via `.local_files[].last_modification` is encoded in ZIP files using [MS-DOS representation](https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-dosdatetimetovarianttime) which lacks a known time zone. Probably the local time/date was used at creation. The `unix_guess` field in `last_modification` is a guess assuming the local time zone was UTC at creation. - -### References -- https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT -- https://opensource.apple.com/source/zip/zip-6/unzip/unzip/proginfo/extra.fld -- https://formats.kaitai.io/dos_datetime/ -- https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-dosdatetimetovarianttime - - -[#]: sh-end - -## Dependency graph - -![alt text](formats.svg "Format diagram") diff --git a/doc/fq.1 b/doc/fq.1 new file mode 100644 index 000000000..5d905a6a4 --- /dev/null +++ b/doc/fq.1 @@ -0,0 +1,6702 @@ +'\" t +.\" Title: fq +.\" Author: Mattias Wadman +.\" Generator: Asciidoctor 2.0.26 +.\" Manual: FQ +.\" Source: FQ +.\" Language: English +.\" +.TH "FQ" "1" "" "FQ" "FQ" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +fq \- tool, language and decoders for working with binary formats +.SH "SYNOPSIS" +.sp +\f(CR\fBfq [\fIOPTIONS\fP] [\-\-] [\fIEXPRESSION\fP] [\fIFILE\fP.\|.\|.]\fP\fP +.SH "DESCRIPTION" +.sp +\fBfq\fP is a tool, language, and decoders for working with binary formats and data. +In most cases it behaves and feels similar to \c +.URL "https://jqlang.github.io/jq/" "jq" "" +and it +also uses the same expression language. +To get the most out of fq it\(cqs recommended to learn more about jq. +.sp +It features a structural hex viewer, nested format decoding, slicing and concatenating +binary data, bit\-level decoding and an interactive REPL with auto\-completion. +.sp +.if n .RS 4 +.nf +.fam C +# Evaluate "d" for file.mp4 +$ fq d file.mp4 + +# Evaluate ".boxes[0].type" for all *.mp4 files +$ fq \*(Aq.boxes[0].type\*(Aq *.mp4 + +# Evaluate "1+2" without reading any input +$ fq \-n 1+2 +.fam +.fi +.if n .RE +.sp +For more advanced usage see \f(CR\-\-arg\fP, \f(CR\-\-slurp\fP, \f(CR\-\-raw\-file\fP, \f(CR\-n\fP etc and examples at the end of the documentation. +.SH "OPTIONS" +.sp +\f(CR\fB\-\-arg NAME VALUE\fP\fP +.RS 4 +Set $NAME to string VALUE +.RE +.sp +\f(CR\fB\-\-argdecode NAME PATH\fP\fP +.RS 4 +Set $NAME to decode of PATH +.RE +.sp +\f(CR\fB\-\-argjson NAME JSON\fP\fP +.RS 4 +Set $NAME to JSON +.RE +.sp +\f(CR\fB\-\-args\fP\fP +.RS 4 +Consume remaining arguments as positional strings +.RE +.sp +\f(CR\fB\-\-color\-output\fP\fP, \f(CR\fB\-C\fP\fP +.RS 4 +Force color output +.RE +.sp +\f(CR\fB\-\-compact\-output\fP\fP, \f(CR\fB\-c\fP\fP +.RS 4 +Use compact output +.RE +.sp +\f(CR\fB\-\-decode\fP\fP, \f(CR\fB\-d NAME\fP\fP +.RS 4 +Decode format or group (probe) +.RE +.sp +\f(CR\fB\-\-from\-file\fP\fP, \f(CR\fB\-f PATH\fP\fP +.RS 4 +Read EXPRESSION from file +.RE +.sp +\f(CR\fB\-\-help\fP\fP, \f(CR\fB\-h [TOPIC]\fP\fP +.RS 4 +Show help for TOPIC (ex: \-h formats, \-h mp4) +.RE +.sp +\f(CR\fB\-\-include\-path\fP\fP, \f(CR\fB\-L PATH\fP\fP +.RS 4 +Add PATH to include search paths +.RE +.sp +\f(CR\fB\-\-join\-output\fP\fP, \f(CR\fB\-j\fP\fP +.RS 4 +No newline after each output +.RE +.sp +\f(CR\fB\-\-jsonargs\fP\fP +.RS 4 +Consume remaining arguments as positional JSON +.RE +.sp +\f(CR\fB\-\-monochrome\-output\fP\fP, \f(CR\fB\-M\fP\fP +.RS 4 +Force monochrome output +.RE +.sp +\f(CR\fB\-\-null\-input\fP\fP, \f(CR\fB\-n\fP\fP +.RS 4 +Null input (use input and inputs to read) +.RE +.sp +\f(CR\fB\-\-option\fP\fP, \f(CR\fB\-o NAME=VALUE/@PATH\fP\fP +.RS 4 +Set option (ex: \-o color=true, see \-\-help options) +.sp +\f(CR\fB\-o addrbase=number\fP\fP +.RS 4 +Number base for addresses +.RE +.sp +\f(CR\fB\-o array_truncate=number\fP\fP +.RS 4 +Array display length to truncate +.RE +.sp +\f(CR\fB\-o bits_format=string\fP\fP +.RS 4 +Raw bits representation +.sp +\f(CR\fB\-o bits_format=base64\fP\fP +.RS 4 +Base64 string. +.RE +.sp +\f(CR\fB\-o bits_format=byte_array\fP\fP +.RS 4 +Array of bytes (zero bit padded if size is not byte aligned). +.RE +.sp +\f(CR\fB\-o bits_format=hex\fP\fP +.RS 4 +Hex string. +.RE +.sp +\f(CR\fB\-o bits_format=md5\fP\fP +.RS 4 +MD5 hex string (zero bit padded). +.RE +.sp +\f(CR\fB\-o bits_format=snippet\fP\fP +.RS 4 +Truncated Base64 string prefixed with bit length. +.RE +.sp +\f(CR\fB\-o bits_format=string\fP\fP +.RS 4 +String with raw bytes (zero bit padded if size is not byte aligned). The string is binary safe internally in fq but bytes not representable as UTF\-8 will be lost if turned into JSON (default). +.RE +.sp +\f(CR\fB\-o bits_format=truncate\fP\fP +.RS 4 +Truncated string. +.RE +.RE +.sp +\f(CR\fB\-o byte_colors=ranges=string,.\|.\|.\fP\fP +.RS 4 +Byte value colorization +.RE +.sp +\f(CR\fB\-o color=true|false\fP\fP +.RS 4 +Use color +.RE +.sp +\f(CR\fB\-o colors=key=value,.\|.\|.\fP\fP +.RS 4 +Color scheme +.RE +.sp +\f(CR\fB\-o compact=true|false\fP\fP +.RS 4 +Use compact JSON +.RE +.sp +\f(CR\fB\-o completion_timeout=number\fP\fP +.RS 4 +Seconds to wait for completion results +.RE +.sp +\f(CR\fB\-o depth=number\fP\fP +.RS 4 +Display tree depth limit +.RE +.sp +\f(CR\fB\-o display_bytes=number\fP\fP +.RS 4 +Display bytes limit +.RE +.sp +\f(CR\fB\-o force=true|false\fP\fP +.RS 4 +Force decode +.RE +.sp +\f(CR\fB\-o join_string=string\fP\fP +.RS 4 +String used to join outputs +.RE +.sp +\f(CR\fB\-o line_bytes=number\fP\fP +.RS 4 +Number of bytes per display line +.RE +.sp +\f(CR\fB\-o raw_string=true|false\fP\fP +.RS 4 +Raw string output +.RE +.sp +\f(CR\fB\-o sizebase=number\fP\fP +.RS 4 +Number base for sizes +.RE +.sp +\f(CR\fB\-o skip_gaps=true|false\fP\fP +.RS 4 +Skip gaps when representing decode value (arrays) as JSON +.RE +.sp +\f(CR\fB\-o string_truncate=number\fP\fP +.RS 4 +String display length truncate +.RE +.sp +\f(CR\fB\-o unicode=true|false\fP\fP +.RS 4 +Use unicode +.RE +.sp +\f(CR\fB\-o verbose=true|false\fP\fP +.RS 4 +Verbose display +.RE +.sp +\f(CR\fB\-o width=number\fP\fP +.RS 4 +Terminal width +.RE +.RE +.sp +\f(CR\fB\-\-raw\-file NAME PATH\fP\fP +.RS 4 +Set $NAME to string content of file +.RE +.sp +\f(CR\fB\-\-raw\-input\fP\fP, \f(CR\fB\-R\fP\fP +.RS 4 +Read raw input strings (don\(cqt decode) +.RE +.sp +\f(CR\fB\-\-raw\-output\fP\fP, \f(CR\fB\-r\fP\fP +.RS 4 +Raw string output (without quotes) +.RE +.sp +\f(CR\fB\-\-raw\-output0\fP\fP +.RS 4 +NUL (zero) byte after each output +.RE +.sp +\f(CR\fB\-\-repl\fP\fP, \f(CR\fB\-i\fP\fP +.RS 4 +Interactive REPL +.RE +.sp +\f(CR\fB\-\-slurp\fP\fP, \f(CR\fB\-s\fP\fP +.RS 4 +Slurp all inputs into an array or string (\-Rs) +.RE +.sp +\f(CR\fB\-\-unicode\-output\fP\fP, \f(CR\fB\-U\fP\fP +.RS 4 +Force unicode output +.RE +.sp +\f(CR\fB\-\-value\-output\fP\fP, \f(CR\fB\-V\fP\fP +.RS 4 +Output JSON value (\-Vr for raw string) +.RE +.sp +\f(CR\fB\-\-version\fP\fP, \f(CR\fB\-v\fP\fP +.RS 4 +Show version +.RE +.SH "CONFIGURATION" +.SS "Init files" +.sp +To add your own functions you can use \f(CRinit.jq\fP that is read from: +.sp +macOS +.RS 4 +\f(CR$HOME/Library/Application Support/fq/init.jq\fP or +.br +\f(CR$HOME/.config/fq/init.jq\fP +.RE +.sp +Linux and BSD +.RS 4 +\f(CR$HOME/.config/fq/init.jq\fP +.RE +.sp +Windows +.RS 4 +\f(CR%AppData%\(rsfq\(rsinit.jq\fP +.RE +.SS "Environment" +.sp +\f(CR\fBNO_COLOR\fP\fP +.RS 4 +Non\-empty string disables color output. +.RE +.sp +\f(CR\fBCLIUNICODE\fP\fP +.RS 4 +Enables use of unicode output characters. +.RE +.sp +\f(CR\fBCOMPLETION_TIMEOUT\fP\fP +.RS 4 +REPL completion timeout in seconds. +.RE +.sp +\f(CR\fBNO_DECODE_PROGRESS\fP\fP +.RS 4 +Disables decode progress indicator. +.RE +.SH "EXPRESSION" +.SS "Syntax" +.sp +See \fBjq\fP(1) for syntax details. But here are some common beginner gotchas: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Functions that take no arguments are called using \f(CRname\fP instead of \f(CRname()\fP. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Arguments are separated by \f(CR;\fP instead of \f(CR,\fP. Comma is used to concatenate output streams. +To call a function \f(CRf\fP with two arguments use \f(CRf(1; 2)\fP. If you do \f(CRf(1, 2)\fP +you pass a single argument \f(CR1, 2\fP, a filter that outputs \f(CR1\fP and then \f(CR2\fP, to \f(CRf\fP. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Expressions can return or "output" zero or more values. This is how iteration etc is done, \f(CR1, 2\fP outputs \f(CR1\fP then \f(CR2\fP. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Similar to shell pipelines, implicit input and output are used and piped together using \f(CR|\fP. \f(CR.\fP is used to refer to +current input. Ex \f(CR1 | . + 2\fP outputs \f(CR3\fP, \f(CR1, 2 | . + 2\fP outputs \f(CR3\fP and \f(CR4\fP. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +In the jq manual and other jq related documentation you might see \f(CRname/2\fP, this means the +function \f(CRname\fP takes two arguments (arity). +.RE +.SS "Additional features" +.sp +fq uses an extended variant of the jq language with a few extra features: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Arbitrary\-precision integers and arithmetics. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Supports raw strings using back\-ticks. String interpolation and codepoint escapes are not processed. +.sp +.if n .RS 4 +.nf +.fam C +`hello \(rs("world")\(rsud83c\(rsudf0d` +.fam +.fi +.if n .RE +.sp +results in the string \f(CRhello \(rs("world")\(rsud83c\(rsudf0d\fP or as JSON \f(CR"hello \(rs\(rs(\(rs"world\(rs")\(rs\(rsud83c\(rs\(rsudf0d"\fP. +In contrast +.sp +.if n .RS 4 +.nf +.fam C +"hello \(rs("world")\(rsud83c\(rsudf0d" +.fam +.fi +.if n .RE +.sp +results in the string \f(CRhello world🌍\fP or as JSON \f(CR"hello world🌍"\fP. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Supports more number bases in integer literals. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR0xabcd\fP (Hexadecimal). +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR0o125715\fP (Octal). +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR0b1010101111001101\fP (Binary). +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Grouping using underscore \f(CR0xab_cd\fP. +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Binary and decode value types, see below. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Try include using an ending question mark \f(CRinclude "file?";\fP that doesn\(cqt fail if file is missing or has errors. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Some values can act as an object with keys even when they are arrays, numbers etc. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +There can be keys hidden from \f(CRkeys\fP and \f(CR[]\fP. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Some values are readonly and can\(cqt be updated or will convert to JSON on update. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Mixing \f(CR\-\-args\fP and \f(CR\-\-jsonargs\fP does not behave the same. +.RE +.SS "Additional functions" +.sp +\f(CR\fBband\fP\fP, \f(CR\fBbor\fP\fP, \f(CR\fBbxor\fP\fP, \f(CR\fBbsl\fP\fP, \f(CR\fBbsr\fP\fP, \f(CR\fBbnot\fP\fP +.RS 4 +Bitwise operations as functions. Works the same as jq\(cqs math functions. Functions that take one argument use +input, \f(CR1 | bnot\fP, and functions with more arguments ignore the input and use formal arguments \f(CRbsl(1; 3)\fP. +.RE +.sp +\f(CR\fBchunk($size)\fP\fP +.RS 4 +Split array or string into \f(CR$size\fP length chunks. Last chunk might be shorter. +.RE +.sp +\f(CR\fBcount\fP\fP, \f(CR\fBcount_by(f)\fP\fP +.RS 4 +Like \f(CRgroup\fP but outputs array of \f(CR[value, count]\fP pairs. +.RE +.sp +\f(CR\fBdelta\fP\fP, \f(CR\fBdelta_by(f)\fP\fP +.RS 4 +Array with difference between consecutive. \f(CRdelta\fP is the same as \f(CRdelta_by(.b \- .a)\fP. +.RE +.sp +\f(CR\fBdiff($a; $b)\fP\fP +.RS 4 +Produce a diff between \f(CR$a\fP and \f(CR$b\fP. Differences are represented as an object \f(CR{a: , b: }\fP. +.RE +.sp +\f(CR\fBexpr_to_path\fP\fP +.RS 4 +Converts from a string \f(CR".key[1]"\fP to a path value \f(CR["key", 1]\fP. +.RE +.sp +\f(CR\fBgrep_by(f)\fP\fP +.RS 4 +Recursively select using a filter and ignore any errors. +Ex: \f(CRgrep_by(. > 180 and . < 200)\fP, \f(CRfirst(grep_by(format == "id3v2"))\fP. +This is the same as doing \f(CR.. | select(f)?\fP. +.RE +.sp +\f(CR\fBgroup\fP\fP +.RS 4 +Group values, same as \f(CRgroup_by(.)\fP. +.RE +.sp +\f(CR\fBpath_to_expr\fP\fP +.RS 4 +Converts a path value \f(CR["key", 1]\fP to a string \f(CR".key[1]"\fP. +.RE +.sp +\f(CR\fBpaste\fP\fP +.RS 4 +Read string from stdin until ^D. Useful for pasting text. Ex: \f(CRpaste | from_pem | asn1_ber | repl\fP read from stdin then decode and start a new sub\-REPL with result. +.RE +.sp +\f(CR\fBstreaks\fP\fP, \f(CR\fBstreaks_by(f)\fP\fP +.RS 4 +Like \f(CRgroup\fP but groups streaks based on condition. +.RE +.sp +\f(CR\fBrepl\fP\fP, \f(CR\fBrepl($opts)\fP\fP +.RS 4 +Nested REPL. Must be last in a pipeline. \f(CRrepl\fP can "slurp" outputs, ex: \f(CR1, 2, 3 | repl\fP, and supports options, ex: \f(CR[1,2,3] | repl({compact: true})\fP. +.RE +.sp +\f(CR\fBslurp("")\fP\fP +.RS 4 +Slurp outputs and saves them to \f(CR$name\fP. Must be last in the pipeline. Will be available as a global array \f(CR$name\fP. Ex \f(CR1,2,3 | slurp("a")\fP, \f(CR$a[]\fP same as \f(CRspew("a")\fP. +.RE +.sp +\f(CR\fBspew\fP\fP, \f(CR\fBspew("")\fP\fP +.RS 4 +Outputs all or a specific slurp. Ex: \f(CRspew("a")\fP. +.RE +.sp +\f(CR\fBprintln\fP\fP, \f(CR\fBprint\fP\fP +.RS 4 +Print string or compact JSON to stdout with and without new line. +.RE +.sp +\f(CR\fBprinterrln\fP\fP, \f(CR\fBprinterr\fP\fP +.RS 4 +Print string or compact JSON to stderr with and without new line. +.RE +.SS "Decode value" +.sp +A decode value is the type returned from decoding a format and used to represent values produced by a decoder. +It can be seen as representing any standard jq type but with some additional properties attached. +.sp +Each decode value has these properties: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Bit range in the input. Can be used as a binary using \f(CRtobytes\fP, \f(CRtobytesrange\fP, \f(CRtobits\fP and \f(CRtobitsrange\fP. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +If scalar type, an actual value: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +This is the decoded representation of the bits, a number, string, bool etc. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Can be accessed using \f(CRtoactual\fP. +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +If scalar type, an optional symbolic value: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Is usually a mapping of the actual to symbolic value, ex: map number to a string value. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Can be accessed using \f(CRtosym\fP. +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +An optional description: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Can be accessed using \f(CRtodescription\fP. +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRparent\fP is the parent decode value +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRparents\fP is all the parent decode values +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtopath\fP is the jq path for the decode value +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtorepr\fP converts decode value to its representation if possible +.RE +.sp +The value of a decode value is the symbolic value if available and otherwise the actual value. To explicitly access the value use \f(CRtovalue\fP. In most expressions this is not needed as it will be done automatically. +.SS "Decode value functions" +.sp +\f(CR\fBroot\fP\fP +.RS 4 +Root decode value for decode value. +.RE +.sp +\f(CR\fBbuffer_root\fP\fP +.RS 4 +Root decode value of sub buffer for decode value. +.RE +.sp +\f(CR\fBformat_root\fP\fP +.RS 4 +Root decode value of nested format for decode value. +.RE +.sp +\f(CR\fBparent\fP\fP +.RS 4 +Parent decode value for decode value. +.RE +.sp +\f(CR\fBparents\fP\fP +.RS 4 +Outputs all parent decode values from decode value. +.RE +.sp +\f(CR\fBtopath\fP\fP +.RS 4 +Path for decode value. Use \f(CRpath_to_expr\fP to get a string representation. +.RE +.sp +\f(CR\fBtovalue\fP, \f(CRtovalue($opts)\fP\fP +.RS 4 +Symbolic, if available, or actual value for decode value. +.RE +.sp +\f(CR\fBtoactual\fP, \f(CRtoactual($opts)\fP\fP +.RS 4 +Actual value for decode value. +.RE +.sp +\f(CR\fBtosym\fP, \f(CRtosym($opts)\fP\fP +.RS 4 +Symbolic value for decode value. +.RE +.sp +\f(CR\fBtodescription\fP\fP +.RS 4 +Description for decode value. +.RE +.sp +\f(CR\fBtorepr\fP\fP +.RS 4 +Converts decode value into what it represents. For example converts msgpack decode value into a value representing its JSON representation. +.RE +.sp +\f(CRtobytes\fP, \f(CRtobytesrange\fP, \f(CRtobits\fP and \f(CRtobitsrange\fP on a decode value will return the raw source bits as a binary. +.SS "Binary" +.sp +Binary type is used to store raw bits or bytes. Raw bits will act as zero bits padded strings in standard jq expressions. +.sp +Use \f(CRtobits\fP and \f(CRtobytes\fP to create them from decode value, string, number or binary array. \f(CRtobytes\fP will if needed zero pad most significant bits to be byte aligned. +.sp +There is also \f(CRtobitsrange\fP and \f(CRtobytesrange\fP which do the same thing but will preserve source range when displayed. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR"string" | tobytes\fP produces a binary with UTF8 bytes. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR1234 | tobits\fP produces a binary with the unsigned big\-endian integer 1234 with enough bits to represent the number. Use \f(CRtobytes\fP to get the same but with enough bytes to represent the number. This is different to how numbers work inside binary arrays where they are limited to 0\-255. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR["abc", 123, .\|.\|.] | tobytes\fP produces a binary from a binary array. See Binary array below. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR.[index]\fP access bit or byte at index \f(CRindex\fP. Index is in units. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[0x12, 0x34, 0x56] | tobytes[1]\fP is \f(CR0x34\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[0x12, 0x34, 0x56] | tobits[3]\fP is \f(CR1\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR.[start:]\fP, \f(CR.[start:end]\fP or \f(CR.[:end]\fP is normal jq slice syntax and will slice the binary from \f(CRstart\fP to \f(CRend\fP. \f(CRstart\fP and \f(CRend\fP are in units. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[0x12, 0x34, 0x56] | tobytes[1:2]\fP will be a binary with the byte \f(CR0x34\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[0x12, 0x34, 0x56] | tobits[4:12]\fP will be a binary with the byte \f(CR0x23\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[0x12, 0x34, 0x56] | tobits[4:20]\fP will be a binary with the bytes \f(CR0x23\fP, \f(CR0x45\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[0x12, 0x34, 0x56] | tobits[4:20] | tobytes[1:]\fP will be a binary with the byte \f(CR0x45\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Both \f(CR.[index]\fP and \f(CR.[start:end]\fP support negative indices to index from end. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRexplode\fP outputs an array with all bytes or bits as integers. +.RE +.SS "Binary functions" +.sp +\f(CR\fBgrep($v)\fP\fP, \f(CR\fBgrep($v; $flags)\fP\fP, \f(CR\fBvgrep($v)\fP\fP, \f(CR\fBvgrep($v; $flags)\fP\fP, \f(CR\fBbgrep($v)\fP\fP, \f(CR\fBbgrep($v; $flags)\fP\fP +.RS 4 +Recursively match \f(CR$v\fP. +\f(CR$v\fP is a scalar to match, where a string is treated as a regexp. A binary will match exact bytes. +\f(CR$flags\fP arguments are regexp flags with additional flag "b" that will treat each byte in the input binary +as a code point. This makes it possible to match exact bytes. +.RE +.sp +\f(CR\fBfgrep($v)\fP\fP, \f(CR\fBfgrep($v; $flags)\fP\fP +.RS 4 +Recursively match field name in a decode value. +.RE +.sp +\f(CR\fBtobits\fP\fP +.RS 4 +Transform input to binary with bit as unit and don\(cqt preserve source range. +.RE +.sp +\f(CR\fBtobitsrange\fP\fP +.RS 4 +Transform input to binary with bit as unit and preserve source range. +.RE +.sp +\f(CR\fBtobytes\fP\fP +.RS 4 +Transform input to binary with byte as unit and don\(cqt preserve source range. +.RE +.sp +\f(CR\fBtobytesrange\fP\fP +.RS 4 +Transform input to binary with byte as unit and preserve source range. +.RE +.sp +\f(CR\fBopen\fP\fP +.RS 4 +Open file for reading. +.RE +.SS "Binary array" +.sp +Binary array is a value "shape" and not a new type. It\(cqs an array of numbers, strings, binaries or other +binary arrays. They can be used as input to \f(CRtobits\fP, \f(CRtobytes\fP or other function that accept a binary as input. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Number is a byte with value 0\-255 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +String as UTF8 bytes +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Binary as is +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Binary array used recursively +.RE +.sp +Binary arrays are similar to and inspired by \c +.URL "https://www.erlang.org/doc/man/erlang.html#type\-iolist" "Erlang iolist" "." +.sp +Some examples: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[0, 123, 255] | tobytes\fP will be binary with 3 bytes 0, 123 and 255. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[0, [123, 255]] | tobytes\fP same as above. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[0, 1, 1, 0, 0, 1, 1, 0 | tobits] | tobytes\fP will be binary with 1 byte, 0x66. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[(.a | tobytes[\-10:]), 255, (.b | tobits[:10])] | tobytes\fP the concatenation of the last 10 bytes of \f(CR.a\fP, byte of value 255 and the first 10 bits of \f(CR.b\fP. +.RE +.SS "Differences to jq" +.sp +See \c +.URL "https://github.com/itchyny/gojq#difference\-to\-jq" "gojq\(cqs differences to jq" "." +.SS "Naming inconsistencies" +.sp +jq\(cqs naming convention is a bit inconsistent. Some standard library functions are named \f(CRtojson\fP while others \f(CRfrom_entries\fP. fq follows this tradition but tries to use \f(CRsnake_case\fP unless there is a good reason. +.sp +Here are all the non\-snake_case functions added by fq. Most of them deal with decode and binary values which are new "primitive" types: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtoactual\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtobits\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtobitsrange\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtobytes\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtobytesrange\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtodescription\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtopath\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtorepr\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtosym\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtovalue\fP +.RE +.SH "DISPLAY OUTPUT" +.sp +\f(CRdisplay\fP or \f(CRd\fP is the main function for displaying values and is also the function that will be used if no other output function is explicitly used. If its input is a decode value it will output a dump and tree structure or otherwise it will output as JSON. +.sp +Below demonstrates some usages: +.sp +The first and second examples do the same thing, inputting \f(CR"hello"\fP to \f(CRdisplay\fP. +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-n \*(Aq"hello"\*(Aq +"hello" + +$ fq \-n \*(Aq"hello" | d\*(Aq +"hello" +.fam +.fi +.if n .RE +.sp +In the next few examples we select out the first "edit list" box in an mp4 file and display it in various ways. +.sp +By default, display will only show the root level: +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aqfirst(grep_by(.type == "elst"))\*(Aq file.mp4 + |00 01 02 03 04 05 06 07 08 09|0123456789|.boxes[3].boxes[1].boxes[1].boxes[0]{}: box +0xd5c|\& 00|\& .|\& size: 28 +0xd66|00 00 1c\& |...\& | +0xd66|\& 65 6c 73 74\& |\& elst\& |\& type: "elst" (An edit list) +0xd66|\& 00\& |\& .\& |\& version: 0 +0xd66|\& 00 00|\& ..|\& flags: 0 +0xd70|00\& |.\& | +0xd70|\& 00 00 00 01\& | ....\& |\& entry_count: 1 +0xd70|\& 00 00 00 28 00|\& ...(.|\& entries[0:1]: +0xd7a|00 00 00 00 01 00 00\& |.......\& | +.fam +.fi +.if n .RE +.sp +First row shows a ruler with byte offset into the line and jq path for the value. +.sp +The columns are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Start address for the line. For example we see that \f(CRsize\fP starts at \f(CR0xd5c\fP (row) + \f(CR0x09\fP (column) = \f(CR0xd65\fP. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Hex representation of input bits for value. Will show the whole byte even if the value only partially uses bits from it. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +ASCII representation of input bits for value. Will show the whole byte even if the value only partially uses bits from it. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Tree structure of decoded value, symbolic value and description. +.RE +.sp +Notation: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{}\fP value is an object that might have nested values. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR[start:end]\fP value is an array with index starting at \f(CRstart\fP and ending at \f(CRend\fP (exclusive). +.RE +.sp +With \f(CRdisplay\fP or \f(CRd\fP it will recursively show the whole tree: +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aqfirst(grep_by(.type == "elst")) | d\*(Aq file.mp4 + |00 01 02 03 04 05 06 07 08 09|0123456789|.boxes[3].boxes[1].boxes[1].boxes[0]{}: box +0xd5c|\& 00|\& .|\& size: 28 +0xd66|00 00 1c\& |...\& | +0xd66|\& 65 6c 73 74\& |\& elst\& |\& type: "elst" (An edit list) +0xd66|\& 00\& |\& .\& |\& version: 0 +0xd66|\& 00 00|\& ..|\& flags: 0 +0xd70|00\& |.\& | +0xd70|\& 00 00 00 01\& | ....\& |\& entry_count: 1 + |\& |\& |\& entries[0:1]: + |\& |\& |\& [0]{}: entry +0xd70|\& 00 00 00 28\& |\& ...( |\& segment_duration: 40 +0xd70|\& 00|\& .|\& media_time: 0 +0xd7a|00 00 00\& |...\& | +0xd7a|\& 00 01 00 00\& |\& ....\& |\& media_rate: 1 +.fam +.fi +.if n .RE +.sp +Same but verbose \f(CRdv\fP: +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aqfirst(grep_by(.type == "elst")) | dv\*(Aq file.mp4 + |00 01 02 03 04 05 06 07 08 09|0123456789|.boxes[3].boxes[1].boxes[1].boxes[0]{}: box 0xd65\-0xd81 (28) +0xd5c|\& 00|\& .|\& size: 28 0xd65\-0xd69 (4) +0xd66|00 00 1c\& |...\& | +0xd66|\& 65 6c 73 74\& |\& elst\& |\& type: "elst" (An edit list) 0xd69\-0xd6d (4) +0xd66|\& 00\& |\& .\& |\& version: 0 0xd6d\-0xd6e (1) +0xd66|\& 00 00|\& ..|\& flags: 0 0xd6e\-0xd71 (3) +0xd70|00\& |.\& | +0xd70|\& 00 00 00 01\& | ....\& |\& entry_count: 1 0xd71\-0xd75 (4) + |\& |\& |\& entries[0:1]: 0xd75\-0xd81 (12) + |\& |\& |\& [0]{}: entry 0xd75\-0xd81 (12) +0xd70|\& 00 00 00 28\& |\& ...( |\& segment_duration: 40 0xd75\-0xd79 (4) +0xd70|\& 00|\& .|\& media_time: 0 0xd79\-0xd7d (4) +0xd7a|00 00 00\& |...\& | +0xd7a|\& 00 01 00 00\& |\& ....\& |\& media_rate: 1 0xd7d\-0xd81 (4) +.fam +.fi +.if n .RE +.sp +In verbose mode bit ranges and array element names are shown. +.sp +Bit ranges use \f(CR[.]\-[.]\fP as notation where \f(CR.\fP is left out if byte aligned. For example \f(CRtype\fP starts at byte \f(CR0xd69\fP bit \f(CR0\fP (\f(CR.0\fP is left out) and ends at \f(CR0xd6d\fP bit \f(CR0\fP (exclusive) and has a size of \f(CR4\fP bytes. +.sp +This verbosely displays the header of the second frame in an mp3 file which has a bunch of non\-byte\-aligned fields: +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aq.frames[1].header | dv\*(Aq file.mp3 + |00 01 02 03 04 05 06 07 08 09|0123456789|.frames[1].header{}: 0xb79\-0xb7d (4) +0xb72|\& ff fb\& |\& .. |\& sync: 0b11111111111 (valid) 0xb79\-0xb7a.3 (1.3) +0xb72|\& fb\& |\& . |\& mpeg_version: "1" (3) (MPEG Version 1) 0xb7a.3\-0xb7a.5 (0.2) +0xb72|\& fb\& |\& . |\& layer: 3 (1) (MPEG Layer 3) 0xb7a.5\-0xb7a.7 (0.2) + |\& |\& |\& sample_count: 1152 +0xb72|\& fb\& |\& . |\& protection_absent: true (No CRC) 0xb7a.7\-0xb7b (0.1) +0xb72|\& 50|\& P|\& bitrate: 64000 (5) 0xb7b\-0xb7b.4 (0.4) +0xb72|\& 50|\& P|\& sample_rate: 44100 (0) 0xb7b.4\-0xb7b.6 (0.2) +0xb72|\& 50|\& P|\& padding: "not_padded" (0b0) 0xb7b.6\-0xb7b.7 (0.1) +0xb72|\& 50|\& P|\& private: 0 0xb7b.7\-0xb7c (0.1) +0xb7c|c4\& |.\& |\& channels: "mono" (0b11) 0xb7c\-0xb7c.2 (0.2) +0xb7c|c4\& |.\& |\& channel_mode: "none" (0b0) 0xb7c.2\-0xb7c.4 (0.2) +0xb7c|c4\& |.\& |\& copyright: 0 0xb7c.4\-0xb7c.5 (0.1) +0xb7c|c4\& |.\& |\& original: 1 0xb7c.5\-0xb7c.6 (0.1) +0xb7c|c4\& |.\& |\& emphasis: "none" (0b0) 0xb7c.6\-0xb7d (0.2) +.fam +.fi +.if n .RE +.sp +Here the \f(CRsync\fP pattern starts at \f(CR0xb79\fP (bit \f(CR0\fP) and ends at \f(CR0xb7a.3\fP (exclusive) and has a size of \f(CR1\fP byte and \f(CR3\fP bits, \f(CR11\fP bits in total (\f(CR8+3\fP). +.sp +There are also some other \f(CRdisplay\fP aliases: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRda\fP is \f(CRdisplay({array_truncate: 0, string_truncate: 0})\fP don\(cqt truncate array and strings. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRdd\fP is \f(CRdisplay({array_truncate: 0, string_truncate: 0, display_bytes: 0})\fP don\(cqt truncate array and strings, show all raw bytes. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRdv\fP is \f(CRdisplay({array_truncate: 0, string_truncate: 0, verbose: true})\fP don\(cqt truncate array and strings and display verbosely. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRddv\fP is \f(CRdisplay({array_truncate: 0, string_truncate: 0, display_bytes: 0, verbose: true})\fP don\(cqt truncate array and strings, show all raw bytes and display verbosely. +.RE +.SH "FORMATS" +.sp +By default fq will try to automatically determine input format. In some cases this might fail or is not +possible, then a format can be specified using \f(CR\-d NAME\fP. It\(cqs possible sometimes to force decode and get +a partial or broken result using \f(CR\-o force=true\fP. +.sp +.if n .RS 4 +.nf +.fam C +# decode as msgpack +$ fq \-d msgpack d file +# force decode as msgpack +$ fq \-d msgpack \-o force=true d file +# see msgpack format help +$ fq \-h msgpack +# list supported formats +$ fq \-h formats +.fam +.fi +.if n .RE +.SS "Format options" +.sp +Some formats has own options that can be set using \f(CR\-o\fP. For +example the \f(CRmp4\fP format has a \f(CRdecode_samples\fP option that controls +if individual samples should be decoded. To disable it one can do +\f(CRfq \-o decode_samples=false . file.mp4\fP. See format list for options. +.SS "Format functions" +.sp +In addition to using \f(CR\-d\fP all format decoders are also available as normal jq functions. +Each format provides multiple functions: +.sp +\f(CR\fP +.RS 4 +Decode and return a decode value even on error. Ex: \f(CR.\|.\|. | mp4\fP +.RE +.sp +\f(CR($options)\fP +.RS 4 +Same as above with format options. Ex: \f(CR.\|.\|. | mp4({decode_samples: false})\fP +.RE +.sp +\f(CRfrom_\fP +.RS 4 +Decode or throw on error. Ex: \f(CR.\|.\|. | from_mp4\fP +.RE +.sp +\f(CRfrom_($options)\fP +.RS 4 +Same as above with format options Ex: \f(CR.\|.\|. | from_mp4({decode_samples: false})\fP +.RE +.sp +Example usage: +.sp +.if n .RS 4 +.nf +.fam C +# decode jpeg found inside some other format +$ fq \*(Aq.some[].query | jpeg\*(Aq file + +# decode jpeg at byte range 100\-200 +$ fq \-d bytes \*(Aq.[100:200] | jpeg\*(Aq file +.fam +.fi +.if n .RE +.SS "Supported formats" +.sp +\f(CR\fBaac_frame\fP\fP +.RS 4 +Advanced Audio Coding frame +.sp +.B Options +.br +.sp +\f(CR\fB\-o object_type=1\fP\fP +.RS 4 +Audio object type +.RE +.RE +.sp +\f(CR\fBadts\fP\fP +.RS 4 +Audio Data Transport Stream +.RE +.sp +\f(CR\fBadts_frame\fP\fP +.RS 4 +Audio Data Transport Stream frame +.RE +.sp +\f(CR\fBaiff\fP\fP +.RS 4 +Audio Interchange File Format +.RE +.sp +\f(CR\fBamf0\fP\fP +.RS 4 +Action Message Format 0 +.RE +.sp +\f(CR\fBapev2\fP\fP +.RS 4 +APEv2 metadata tag +.RE +.sp +\f(CR\fBapple_bookmark\fP\fP +.RS 4 +Apple BookmarkData +.sp +Apple\(cqs \f(CRbookmarkData\fP format is used to encode information that can be resolved into a \f(CRURL\fP object for a file even if the user moves or renames it. Can also contain security scoping information for App Sandbox support. +These \f(CRbookmarkData\fP blobs are often found encoded in data fields of Binary Property Lists. Notable examples include: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRcom.apple.finder.plist\fP \- contains an \f(CRFXRecentFolders\fP value, which is an array of ten objects, each of which consists of a \f(CRname\fP and \f(CRfile\-bookmark\fP field, which is a \f(CRbookmarkData\fP object for each recently accessed folder location. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRcom.apple.LSSharedFileList.RecentApplications.sfl2\fP \- \f(CRsfl2\fP files are actually \f(CRplist\fP files of the \f(CRNSKeyedArchiver\fP format. They can be parsed the same as \f(CRplist\fP files, but they have a more complicated tree\-like structure than would typically be found, which can make locating and retrieving specific values difficult, even once it has been converted to a JSON representation. For more information about these types of files, see Sarah Edwards\*(Aq excellent research on the subject (link in references). +.RE +.sp +\f(CRfq\(cqs `grep_by\fP function can be used to recursively descend through the decoded tree, probing for and selecting any \f(CRbookmark\fP blobs, then converting them to readable JSON with \f(CRtorepr\fP: +.sp +.if n .RS 4 +.nf +.fam C +fq \*(Aqgrep_by(.type=="data" and .value[0:4] == "book") | .value | apple_bookmark | +torepr\*(Aq +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +David McDonald \c +.URL "https://github.com/dgmcdona" "@dgmcdona" " " +.URL "https://twitter.com/river_rat_504" "@river_rat_504" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://developer.apple.com/documentation/foundation/url/2143023\-bookmarkdata" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://mac\-alias.readthedocs.io/en/latest/bookmark_fmt.html" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.mac4n6.com/blog/2016/1/1/manual\-analysis\-of\-nskeyedarchiver\-formatted\-plist\-files\-a\-review\-of\-the\-new\-os\-x\-1011\-recent\-items" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://michaellynn.github.io/2015/10/24/apples\-bookmarkdata\-exposed/" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBar\fP\fP +.RS 4 +Unix archive +.RE +.sp +\f(CR\fBasn1_ber\fP\fP +.RS 4 +ASN1 BER (basic encoding rules, also CER and DER) +.sp +Supports decoding BER, CER and DER (X.690). +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Currently no extra validation is done for CER and DER. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Does not support specifying a schema. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Supports \f(CRtorepr\fP but without schema all sequences and sets will be arrays. +.RE +.sp +Can be used to decode certificates etc +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d bytes \*(Aqfrom_pem | asn1_ber | d\*(Aq cert.pem +.fam +.fi +.if n .RE +.sp +Can decode nested values +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d asn1_ber \*(Aq.constructed[1].value | asn1_ber\*(Aq file.ber +.fam +.fi +.if n .RE +.sp +Manual schema +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d asn1_ber \*(Aqtorepr as $r | ["version", "modulus", "private_exponent", "prime1", "prime2", "exponent1", "exponent2", "coefficient"] | with_entries({key: .value, value: $r[.key]})\*(Aq pkcs1.der +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.itu.int/ITU\-T/studygroups/com10/languages/X.690_1297.pdf" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://en.wikipedia.org/wiki/X.690" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://letsencrypt.org/docs/a\-warm\-welcome\-to\-asn1\-and\-der/" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://lapo.it/asn1js/" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBav1_ccr\fP\fP +.RS 4 +AV1 Codec Configuration Record +.RE +.sp +\f(CR\fBav1_frame\fP\fP +.RS 4 +AV1 frame +.RE +.sp +\f(CR\fBav1_obu\fP\fP +.RS 4 +AV1 Open Bitstream Unit +.RE +.sp +\f(CR\fBavc_annexb\fP\fP +.RS 4 +H.264/AVC Annex B +.RE +.sp +\f(CR\fBavc_au\fP\fP +.RS 4 +H.264/AVC Access Unit +.sp +.B Options +.br +.sp +\f(CR\fB\-o bottom_field_pic_order_in_frame_present_flag=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o cpb_cnt=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o cpb_removal_delay_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o delta_pic_order_always_zero_flag=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o dpb_output_delay_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o frame_mbs_only_flag=true\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o initial_cpb_removal_delay_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o length_size=0\fP\fP +.RS 4 +Length value size +.RE +.sp +\f(CR\fB\-o log2max_frame_num=4\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o log2max_pic_order_cnt_lsb=4\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o nal_hrd_parameters_present=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o pic_order_cnt_type=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o redundant_pic_cnt_present_flag=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o separate_colour_plane_flag=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o time_offset_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o vcl_hrd_parameters_present=false\fP\fP +.RS 4 +No description +.RE +.RE +.sp +\f(CR\fBavc_dcr\fP\fP +.RS 4 +H.264/AVC Decoder Configuration Record +.RE +.sp +\f(CR\fBavc_nalu\fP\fP +.RS 4 +H.264/AVC Network Access Layer Unit +.sp +.B Options +.br +.sp +\f(CR\fB\-o bottom_field_pic_order_in_frame_present_flag=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o cpb_cnt=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o cpb_removal_delay_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o delta_pic_order_always_zero_flag=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o dpb_output_delay_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o frame_mbs_only_flag=true\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o initial_cpb_removal_delay_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o log2max_frame_num=4\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o log2max_pic_order_cnt_lsb=4\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o nal_hrd_parameters_present=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o pic_order_cnt_type=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o redundant_pic_cnt_present_flag=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o separate_colour_plane_flag=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o time_offset_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o vcl_hrd_parameters_present=false\fP\fP +.RS 4 +No description +.RE +.RE +.sp +\f(CR\fBavc_pps\fP\fP +.RS 4 +H.264/AVC Picture Parameter Set +.RE +.sp +\f(CR\fBavc_sei\fP\fP +.RS 4 +H.264/AVC Supplemental Enhancement Information +.sp +.B Options +.br +.sp +\f(CR\fB\-o cpb_cnt=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o cpb_removal_delay_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o delta_pic_order_always_zero_flag=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o dpb_output_delay_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o frame_mbs_only_flag=true\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o initial_cpb_removal_delay_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o log2max_frame_num=4\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o log2max_pic_order_cnt_lsb=4\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o nal_hrd_parameters_present=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o pic_order_cnt_type=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o separate_colour_plane_flag=false\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o time_offset_length=0\fP\fP +.RS 4 +No description +.RE +.sp +\f(CR\fB\-o vcl_hrd_parameters_present=false\fP\fP +.RS 4 +No description +.RE +.RE +.sp +\f(CR\fBavc_sps\fP\fP +.RS 4 +H.264/AVC Sequence Parameter Set +.RE +.sp +\f(CR\fBavi\fP\fP +.RS 4 +Audio Video Interleaved +.sp +Samples +.RS 4 +AVI has many redundant ways to index samples so currently \f(CR.streams[].samples\fP will only include samples the most "modern" way used in the file. That is in order of stream super index, movi ix index then idx1 index. +.RE +.sp +Extract samples for stream 1 +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aq.streams[1].samples[] | tobytes\*(Aq file.avi > stream01.mp3 +.fam +.fi +.if n .RE +.sp +Show stream summary +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-o decode_samples=false \*(Aq[.chunks[0] | grep_by(.id=="LIST" and .type=="strl") | grep_by(.id=="strh") as {$type} | grep_by(.id=="strf") as {$format_tag, $compression} | {$type,$format_tag,$compression}]\*(Aq *.avi +.fam +.fi +.if n .RE +.sp +Speed up decoding by disabling sample and extended chunks decoding +.RS 4 +If you\(cqre not interested in sample details or extended chunks you can speed up decoding by using: +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-o decode_samples=false \-o decode_extended_chunks=false d file.avi +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://learn.microsoft.com/en\-us/windows/win32/directshow/avi\-riff\-file\-reference" "AVI RIFF File Reference" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "http://www.jmcgowan.com/odmlff2.pdf" "OpenDML AVI File Format Extensions" "" +.RE +.RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o decode_extended_chunks=true\fP\fP +.RS 4 +Decode extended chunks +.RE +.sp +\f(CR\fB\-o decode_samples=true\fP\fP +.RS 4 +Decode samples +.RE +.RE +.sp +\f(CR\fBavro_ocf\fP\fP +.RS 4 +Avro object container file +.sp +Supports reading Avro Object Container Format (OCF) files based on the 1.11.0 specification. +Capable of handling null, deflate, and snappy codecs for data compression. +Limitations: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Schema does not support self\-referential types, only built\-in types. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Decimal logical types are not supported for decoding, will just be treated as their primitive type +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://avro.apache.org/docs/current/spec.html#Object+Container+Files" "" "" +.RE +.RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Xentripetal \c +.MTO "xentripetal\(atfastmail.com" "" " " +.URL "https://github.com/xentripetal" "@xentripetal" "" +.RE +.RE +.RE +.sp +\f(CR\fBbencode\fP\fP +.RS 4 +BitTorrent bencoding +.sp +Convert represented value to JSON +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d bencode torepr file.torrent +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://wiki.theory.org/BitTorrentSpecification#Bencoding" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBbitcoin_blkdat\fP\fP +.RS 4 +Bitcoin blk.dat +.RE +.sp +\f(CR\fBbitcoin_block\fP\fP +.RS 4 +Bitcoin block +.sp +.B Options +.br +.sp +\f(CR\fB\-o has_header=false\fP\fP +.RS 4 +Has blkdat header +.RE +.RE +.sp +\f(CR\fBbitcoin_script\fP\fP +.RS 4 +Bitcoin script +.RE +.sp +\f(CR\fBbitcoin_transaction\fP\fP +.RS 4 +Bitcoin transaction +.RE +.sp +\f(CR\fBbits\fP\fP +.RS 4 +Raw bits +.sp +Decode to a slice and indexable binary of bits. +.sp +Slice and decode bit range +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ echo \*(Aqsome {"a":1} json\*(Aq | fq \-d bits \*(Aq.[40:\-48] | fromjson\*(Aq +{ + "a": 1 +} +.fam +.fi +.if n .RE +.sp +Index bits +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ echo \*(Aqhello\*(Aq | fq \-d bits \*(Aq.[4]\*(Aq +1 +$ echo \*(Aqhello\*(Aq | fq \-c \-d bits \*(Aq[.[range(8)]]\*(Aq +[0,1,1,0,1,0,0,0] +.fam +.fi +.if n .RE +.RE +.sp +\f(CR\fBbplist\fP\fP +.RS 4 +Apple Binary Property List +.sp +Show full decoding +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq d Info.plist +.fam +.fi +.if n .RE +.sp +Timestamps +.RS 4 +Timestamps in Apple Binary Property Lists are encoded as Cocoa Core Data timestamps, where the raw value is the floating point number of seconds since January 1, 2001. By default, \f(CRfq\fP will render the raw floating point value. In order to get the raw value or the string description, use the \f(CRtovalue\fP or \f(CRtodescription\fP functions: +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aqtorepr.SomeTimeStamp | tovalue\*(Aq Info.plist +685135328 + +$ fq \*(Aqtorepr.SomeTimeStamp | todescription\*(Aq Info.plist +"2022\-09\-17T19:22:08Z" +.fam +.fi +.if n .RE +.sp +Get JSON representation +.RS 4 +\f(CRbplist\fP files can be converted to a JSON representation using the \f(CRtorepr\fP filter: +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq torepr com.apple.UIAutomation.plist +{ + "UIAutomationEnabled": true +} +.fam +.fi +.if n .RE +.sp +Decoding NSKeyedArchiver serialized objects +.RS 4 +A common way that Swift and Objective\-C libraries on macOS serialize objects is through the NSKeyedArchiver API, which flattens objects into a list of elements and class descriptions that are reconstructed into an object graph using CFUID elements in the property list. \f(CRfq\fP includes a function, \f(CRfrom_ns_keyed_archiver\fP, which will rebuild this object graph into a friendly representation. +If no parameters are supplied, it will assume that there is a CFUID located at \f(CR."$top".root\fP that specifies the root from which decoding should occur. If this is not present, an error will be produced, asking the user to specify a root object in the \f(CR.$objects\fP list from which to decode. +The following examples show how this might be used (in this case, within the \f(CRfq\fP REPL): +.RE +.sp +.if n .RS 4 +.nf +.fam C +# Assume $top.root is present +bplist> from_ns_keyed_archiver + +# Specify optional root +bplist> from_ns_keyed_archiver(1) +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +David McDonald \c +.URL "https://github.com/dgmcdona" "@dgmcdona" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "http://fileformats.archiveteam.org/wiki/Property_List/Binary" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://medium.com/@karaiskc/understanding\-apples\-binary\-property\-list\-format\-281e6da00dbd" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://opensource.apple.com/source/CF/CF\-550/CFBinaryPList.c" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBbsd_loopback_frame\fP\fP +.RS 4 +BSD loopback frame +.RE +.sp +\f(CR\fBbson\fP\fP +.RS 4 +Binary JSON +.sp +Limitations +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +The decimal128 type is not supported for decoding, will just be treated as binary +.RE +.RE +.sp +Convert represented value to JSON +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d bson torepr file.bson +.fam +.fi +.if n .RE +.sp +Filter represented value +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d bson \*(Aqtorepr | select(.name=="bob")\*(Aq file.bson +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Mattias Wadman \c +.MTO "mattias.wadman\(atgmail.com" "" "," +original author +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Matt Dale \c +.URL "https://github.com/matthewdale" "@matthewdale" "," +additional types and bug fixes +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://bsonspec.org/spec.html" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBbytes\fP\fP +.RS 4 +Raw bytes +.sp +Decode to a slice and indexable binary of bytes. +.sp +Slice out byte ranges +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ echo \-n \*(Aqhello\*(Aq | fq \-d bytes \*(Aq.[\-3:]\*(Aq > last_3_bytes +$ echo \-n \*(Aqhello\*(Aq | fq \-d bytes \*(Aq[.[\-2:], .[0:2]] | tobytes\*(Aq > first_last_2_bytes_swapped +.fam +.fi +.if n .RE +.sp +Slice and decode byte range +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ echo \*(Aqsome {"a":1} json\*(Aq | fq \-d bytes \*(Aq.[5:\-6] | fromjson\*(Aq +{ + "a": 1 +} +.fam +.fi +.if n .RE +.sp +Index bytes +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ echo \*(Aqhello\*(Aq | fq \-d bytes \*(Aq.[1]\*(Aq +101 +.fam +.fi +.if n .RE +.RE +.sp +\f(CR\fBbzip2\fP\fP +.RS 4 +bzip2 compression +.RE +.sp +\f(CR\fBcaff\fP\fP +.RS 4 +Live2D Cubism archive +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/ronsor" "@ronsor" "" +.RE +.RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o uncompress=true\fP\fP +.RS 4 +Uncompress and probe files +.RE +.RE +.sp +\f(CR\fBcbor\fP\fP +.RS 4 +Concise Binary Object Representation +.sp +Convert represented value to JSON +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d cbor torepr file.cbor +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://en.wikipedia.org/wiki/CBOR" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.rfc\-editor.org/rfc/rfc8949.html" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBcsv\fP\fP +.RS 4 +Comma separated values +.sp +TSV to CSV +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d csv \-o comma="\(rst" to_csv file.tsv +.fam +.fi +.if n .RE +.sp +Convert rows to objects based on header row +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d csv \*(Aq.[0] as $t | .[1:] | map(with_entries(.key = $t[.key]))\*(Aq file.csv +.fam +.fi +.if n .RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o comma=","\fP\fP +.RS 4 +Separator character +.RE +.sp +\f(CR\fB\-o comment="#"\fP\fP +.RS 4 +Comment line character +.RE +.RE +.sp +\f(CR\fBdns\fP\fP +.RS 4 +DNS packet +.RE +.sp +\f(CR\fBdns_tcp\fP\fP +.RS 4 +DNS packet (TCP) +.RE +.sp +\f(CR\fBelf\fP\fP +.RS 4 +Executable and Linkable Format +.RE +.sp +\f(CR\fBether8023_frame\fP\fP +.RS 4 +Ethernet 802.3 frame +.RE +.sp +\f(CR\fBexif\fP\fP +.RS 4 +Exchangeable Image File Format +.RE +.sp +\f(CR\fBfairplay_spc\fP\fP +.RS 4 +FairPlay Server Playback Context +.RE +.sp +\f(CR\fBfit\fP\fP +.RS 4 +Garmin Flexible and Interoperable Data Transfer +.sp +Limitations +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Fields with subcomponents, such as "compressed_speed_distance" field on globalMessageNumber 20 is not represented correctly. The field is read as 3 separate bytes where the first 12 bits are speed and the last 12 bits are distance. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +There are still lots of UNKNOWN fields due to gaps in Garmin\(cqs SDK Profile documentation. (Currently FIT SDK 21.126) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Compressed timestamp messages are not accumulated against last known full timestamp. +.RE +.RE +.sp +Convert stream of data messages to JSON array +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aq[.data_records[] | select(.record_header.message_type == "data").data_message]\*(Aq file.fit +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Mikael Lofjärd \c +.MTO "mikael.lofjard\(atgmail.com" "" "," +original author +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://developer.garmin.com/fit/protocol/" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://developer.garmin.com/fit/cookbook/decoding\-activity\-files/" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBflac\fP\fP +.RS 4 +Free Lossless Audio Codec file +.RE +.sp +\f(CR\fBflac_frame\fP\fP +.RS 4 +FLAC frame +.sp +.B Options +.br +.sp +\f(CR\fB\-o bits_per_sample=16\fP\fP +.RS 4 +Bits per sample +.RE +.sp +\f(CR\fB\-o sample_details=false\fP\fP +.RS 4 +Decode more sample details like residuals etc +.RE +.RE +.sp +\f(CR\fBflac_metadatablock\fP\fP +.RS 4 +FLAC metadatablock +.RE +.sp +\f(CR\fBflac_metadatablocks\fP\fP +.RS 4 +FLAC metadatablocks +.RE +.sp +\f(CR\fBflac_picture\fP\fP +.RS 4 +FLAC metadatablock picture +.RE +.sp +\f(CR\fBflac_streaminfo\fP\fP +.RS 4 +FLAC streaminfo +.RE +.sp +\f(CR\fBgif\fP\fP +.RS 4 +Graphics Interchange Format +.RE +.sp +\f(CR\fBgzip\fP\fP +.RS 4 +gzip compression +.RE +.sp +\f(CR\fBheif\fP\fP +.RS 4 +High Efficiency Image Format +.sp +.B Options +.br +.sp +\f(CR\fB\-o allow_truncated=false\fP\fP +.RS 4 +Allow box to be truncated +.RE +.RE +.sp +\f(CR\fBhevc_annexb\fP\fP +.RS 4 +H.265/HEVC Annex B +.RE +.sp +\f(CR\fBhevc_au\fP\fP +.RS 4 +H.265/HEVC Access Unit +.sp +.B Options +.br +.sp +\f(CR\fB\-o length_size=4\fP\fP +.RS 4 +Length value size +.RE +.RE +.sp +\f(CR\fBhevc_dcr\fP\fP +.RS 4 +H.265/HEVC Decoder Configuration Record +.RE +.sp +\f(CR\fBhevc_nalu\fP\fP +.RS 4 +H.265/HEVC Network Access Layer Unit +.RE +.sp +\f(CR\fBhevc_pps\fP\fP +.RS 4 +H.265/HEVC Picture Parameter Set +.RE +.sp +\f(CR\fBhevc_sps\fP\fP +.RS 4 +H.265/HEVC Sequence Parameter Set +.RE +.sp +\f(CR\fBhevc_vps\fP\fP +.RS 4 +H.265/HEVC Video Parameter Set +.RE +.sp +\f(CR\fBhtml\fP\fP +.RS 4 +HyperText Markup Language +.sp +HTML is decoded in HTML5 mode and will always include \f(CR\fP, \f(CR\fP and \f(CR\fP elements. +See xml format for more examples and how to preserve element order and how to encode to xml. +There is no \f(CRto_html\fP function, see \f(CRto_xml\fP instead. +.sp +Element as object +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +# decode as object is the default +$ echo \*(Aqtext\*(Aq | fq \-d html +{ + "html": { + "body": { + "a": { + "#text": "text", + "@href": "url" + } + }, + "head": "" + } +} +.fam +.fi +.if n .RE +.sp +Element as array +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ echo \*(Aqtext\*(Aq | fq \-d html \-o array=true +[ + "html", + null, + [ + [ + "head", + null, + [] + ], + [ + "body", + null, + [ + [ + "a", + { + "#text": "text", + "href": "url" + }, + [] + ] + ] + ] + ] +] + +# decode html files to a {file: "title", ...} object +$ fq \-n \-d html \*(Aq[inputs | {key: input_filename, value: .html.head.title?}] | from_entries\*(Aq *.html + +# href:s in file +$ fq \-r \-o array=true \-d html \*(Aq.. | select(.[0] == "a" and .[1].href)?.[1].href\*(Aq file.html +.fam +.fi +.if n .RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o array=false\fP\fP +.RS 4 +Decode as nested arrays +.RE +.sp +\f(CR\fB\-o attribute_prefix="@"\fP\fP +.RS 4 +Prefix for attribute keys +.RE +.sp +\f(CR\fB\-o seq=false\fP\fP +.RS 4 +Use seq attribute to preserve element order +.RE +.RE +.sp +\f(CR\fBicc_profile\fP\fP +.RS 4 +International Color Consortium profile +.RE +.sp +\f(CR\fBicmp\fP\fP +.RS 4 +Internet Control Message Protocol +.RE +.sp +\f(CR\fBicmpv6\fP\fP +.RS 4 +Internet Control Message Protocol v6 +.RE +.sp +\f(CR\fBid3v1\fP\fP +.RS 4 +ID3v1 metadata +.RE +.sp +\f(CR\fBid3v11\fP\fP +.RS 4 +ID3v1.1 metadata +.RE +.sp +\f(CR\fBid3v2\fP\fP +.RS 4 +ID3v2 metadata +.RE +.sp +\f(CR\fBipv4_packet\fP\fP +.RS 4 +Internet protocol v4 packet +.RE +.sp +\f(CR\fBipv6_packet\fP\fP +.RS 4 +Internet protocol v6 packet +.RE +.sp +\f(CR\fBjp2c\fP\fP +.RS 4 +JPEG 2000 codestream +.RE +.sp +\f(CR\fBjpeg\fP\fP +.RS 4 +Joint Photographic Experts Group file +.RE +.sp +\f(CR\fBjson\fP\fP +.RS 4 +JavaScript Object Notation +.RE +.sp +\f(CR\fBjsonl\fP\fP +.RS 4 +JavaScript Object Notation Lines +.RE +.sp +\f(CR\fBleveldb_descriptor\fP\fP +.RS 4 +LevelDB Descriptor +.sp +Limitations +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +fragmented non\-"full" records are not merged and decoded further. +.RE +.RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/mikez" "@mikez" "," +original author +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/google/leveldb/blob/main/doc/impl.md#manifest" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/google/leveldb/blob/main/doc/log_format.md" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/google/leveldb/blob/main/db/version_edit.cc" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBleveldb_log\fP\fP +.RS 4 +LevelDB Log +.sp +Limitations +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +fragmented non\-"full" records are not merged and decoded further. +.RE +.RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/mikez" "@mikez" "," +original author +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/google/leveldb/blob/main/doc/impl.md#log\-files" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/google/leveldb/blob/main/doc/log_format.md" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/google/leveldb/blob/main/db/write_batch.cc" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBleveldb_table\fP\fP +.RS 4 +LevelDB Table +.sp +Limitations +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +no Meta Blocks (like "filter") are decoded yet. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Zstandard uncompression is not implemented yet. +.RE +.RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/mikez" "@mikez" "," +original author +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/google/leveldb/blob/main/doc/table_format.md" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/google/leveldb/blob/main/doc/impl.md" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/google/leveldb/blob/main/doc/index.md" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBluajit\fP\fP +.RS 4 +LuaJIT 2.0 bytecode +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/dlatchx" "@dlatchx" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/LuaJIT/LuaJIT/blob/v2.1/src/lj_bcdump.h" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "http://scm.zoomquiet.top/data/20131216145900/index.html" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBmacho\fP\fP +.RS 4 +Mach\-O macOS executable +.sp +Supports decoding vanilla and FAT Mach\-O binaries. +.sp +Select 64bit load segments +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aq.load_commands[] | select(.cmd=="segment_64")\*(Aq file +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/aidansteele/osx\-abi\-macho\-file\-format\-reference" "" "" +.RE +.RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Sıddık AÇIL \c +.MTO "acils\(atitu.edu.tr" "" " " +.URL "https://github.com/Akaame" "@Akaame" "" +.RE +.RE +.RE +.sp +\f(CR\fBmacho_fat\fP\fP +.RS 4 +Fat Mach\-O macOS executable (multi\-architecture) +.RE +.sp +\f(CR\fBmarkdown\fP\fP +.RS 4 +Markdown +.sp +Array with all level 1 and 2 headers +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d markdown \*(Aq[.. | select(.type=="heading" and .level<=2)?.children[0]]\*(Aq file.md +.fam +.fi +.if n .RE +.RE +.sp +\f(CR\fBmatroska\fP\fP +.RS 4 +Matroska file +.sp +Lookup element using path +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aqmatroska_path(".Segment.Tracks[0]")\*(Aq file.mkv +.fam +.fi +.if n .RE +.sp +Get path to element +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aqgrep_by(.id == "Tracks") | matroska_path\*(Aq file.mkv +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.rfc\-editor.org/info/rfc8794" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://matroska.org/technical/specs/index.html" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.matroska.org/technical/basics.html" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.matroska.org/technical/codec_specs.html" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://wiki.xiph.org/MatroskaOpus" "" "" +.RE +.RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o decode_samples=true\fP\fP +.RS 4 +Decode samples +.RE +.RE +.sp +\f(CR\fBmidi\fP\fP +.RS 4 +Standard MIDI file +.sp +Notes +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Only supports the MIDI 1.0 MIDI file specification. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Only supports \fIMThd\fP and \fIMTrk\fP chunks. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Does only basic validation on the MIDI data. +.RE +.RE +.sp +Sample queries +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Extract the track names from a MIDI file +.RE +.RE +.sp +.if n .RS 4 +.nf +.fam C +fq \-d midi \*(Aq.. | select(.event=="track_name")? | "\(rs(.track_name)"\*(Aq midi/twinkle.mid +.fam +.fi +.if n .RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Extract the tempo changes from a MIDI file +.RE +.sp +.if n .RS 4 +.nf +.fam C +fq \-d midi \*(Aq.. | select(.event=="tempo")?.tempo\*(Aq midi/twinkle.mid +.fam +.fi +.if n .RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Extract the key changes from a MIDI file +.RE +.sp +.if n .RS 4 +.nf +.fam C +fq \-d midi \*(Aq.. | select(.event=="key_signature")?.key_signature\*(Aq midi/twinkle.mid +.fam +.fi +.if n .RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Extract NoteOn events: +.RE +.sp +.if n .RS 4 +.nf +.fam C +fq \-d midi \*(Aqgrep_by(.event=="note_on") | [.time.tick, .note_on.note] | join(" ")\*(Aq midi/twinkle.mid +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/transcriptaze" "transcriptaze" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.midi.org/specifications/item/the\-midi\-1\-0\-specification" "The Complete MIDI 1.0 Detailed Specification" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://midi.org/standard\-midi\-files" "Standard MIDI Files" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "http://midi.teragonaudio.com/tech/midifile.htm" "Standard MIDI File (SMF) Format" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "http://www.somascape.org/midi/tech/mfile.html" "MIDI Files Specification" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.recordingblogs.com/wiki/midi\-smpte\-offset\-meta\-message" "MIDI SMPTE Offset meta message" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "http://www.somascape.org/midi/tech/mfile.html#meta" "Somascape MIDI Files Specification" "" +.RE +.RE +.RE +.sp +\f(CR\fBmoc3\fP\fP +.RS 4 +MOC3 file +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/ronsor" "@ronsor" "" +.RE +.RE +.RE +.sp +\f(CR\fBmp3\fP\fP +.RS 4 +MP3 file +.sp +.B Options +.br +.sp +\f(CR\fB\-o max_sync_seek=32768\fP\fP +.RS 4 +Max byte distance to next sync +.RE +.sp +\f(CR\fB\-o max_unique_header_configs=5\fP\fP +.RS 4 +Max number of unique frame header configs allowed +.RE +.sp +\f(CR\fB\-o max_unknown=50\fP\fP +.RS 4 +Max percent (0\-100) unknown bits +.RE +.RE +.sp +\f(CR\fBmp3_frame\fP\fP +.RS 4 +MPEG audio layer 3 frame +.RE +.sp +\f(CR\fBmp3_frame_vbri\fP\fP +.RS 4 +MP3 frame Fraunhofer encoder variable bitrate tag +.RE +.sp +\f(CR\fBmp3_frame_xing\fP\fP +.RS 4 +MP3 frame Xing/Info tag +.RE +.sp +\f(CR\fBmp4\fP\fP +.RS 4 +ISOBMFF, QuickTime and similar +.sp +Speed up decoding by not decoding samples +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +# manually decode first sample as an aac_frame +$ fq \-o decode_samples=false \*(Aq.tracks[0].samples[0] | aac_frame | d\*(Aq file.mp4 +.fam +.fi +.if n .RE +.sp +Entries for first edit list as values +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aqfirst(grep_by(.type=="elst").entries) | tovalue\*(Aq file.mp4 +.fam +.fi +.if n .RE +.sp +Whole box tree as JSON (exclude mdat data and tracks) +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aqdel(.tracks) | grep_by(.type=="mdat").data = "" | tovalue\*(Aq file.mp4 +.fam +.fi +.if n .RE +.sp +Force decode a single box +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-n \*(Aq"AAAAHGVsc3QAAAAAAAAAAQAAADIAAAQAAAEAAA==" | from_base64 | mp4({force:true}) | d\*(Aq +.fam +.fi +.if n .RE +.sp +Lookup mp4 box using an mp4 box path +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +# | mp4_path($path) \-> +$ fq \*(Aqmp4_path(".moov.trak[1]")\*(Aq file.mp4 +.fam +.fi +.if n .RE +.sp +Get mp4 box path for a decode value box +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +# | mp4_path \-> string +$ fq \*(Aqgrep_by(.type == "trak") | mp4_path\*(Aq file.mp4 +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://en.wikipedia.org/wiki/ISO/IEC_base_media_file_format" "ISO/IEC base media file format (MPEG\-4 Part 12)" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://developer.apple.com/standards/qtff\-2001.pdf" "Quicktime file format" "" +.RE +.RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o allow_truncated=false\fP\fP +.RS 4 +Allow box to be truncated +.RE +.sp +\f(CR\fB\-o decode_samples=true\fP\fP +.RS 4 +Decode track samples +.RE +.sp +\f(CR\fB\-o skip_samples=false\fP\fP +.RS 4 +Skip track samples +.RE +.RE +.sp +\f(CR\fBmpeg_asc\fP\fP +.RS 4 +MPEG\-4 Audio Specific Config +.RE +.sp +\f(CR\fBmpeg_es\fP\fP +.RS 4 +MPEG Elementary Stream +.RE +.sp +\f(CR\fBmpeg_pes\fP\fP +.RS 4 +MPEG Packetized elementary stream +.RE +.sp +\f(CR\fBmpeg_pes_packet\fP\fP +.RS 4 +MPEG Packetized elementary stream packet +.RE +.sp +\f(CR\fBmpeg_spu\fP\fP +.RS 4 +Sub Picture Unit (DVD subtitle) +.RE +.sp +\f(CR\fBmpeg_ts\fP\fP +.RS 4 +MPEG Transport Stream +.RE +.sp +\f(CR\fBmsgpack\fP\fP +.RS 4 +MessagePack +.sp +Convert represented value to JSON +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d msgpack torepr file.msgpack +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/msgpack/msgpack/blob/master/spec.md" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBnegentropy\fP\fP +.RS 4 +Negentropy message +.sp +View a full Negentropy message +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d negentropy dd file +.fam +.fi +.if n .RE +.sp +Or from hex +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ echo \*(Aq6186b7abb47c0001108e4206828ee3bf34258465809a337c6c00019a68e37b177a50b3ae7164ccc628b962020114019c1381281c9e3849d5fbd514b7bb65ad0101e601fbf7451f5d22e7fa36ae3e910e9f5215020157014a1b26853e06e9c32eb41b1df4f9ab300201e6011840e273c84bb1344f1d4e15d9aa67920200016f12ee2340888653f10b0ec2d438ac9f0101840156d2d796f4dff004ab369b9bcfa4d81e020187013f1b3c8a019800d5764e2de6bdfd2785020114017caaf0acb5dfe249aa0f7f742402168a01018301e7b8c4decb1eae455ca5714281e3245302017a01409c22636b097362df125ddffb6d944302015b01f332208bee82acf8ed922853ee54057f020001fc3e51fdb0b92966e38017f7959903850101cc01428ce0c96d49f15b50143e4fb228cb9300000131712d30e5296a7a45d07bba452d61cd\*(Aq | fq \-R \*(Aqfrom_hex | negentropy | dd\*(Aq +.fam +.fi +.if n .RE +.sp +Check how many ranges the message has and how many of those are of \*(Aqfingerprint\*(Aq mode +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d negentropy \*(Aq.bounds | length as $total | map(select(.mode == "fingerprint")) | length | {$total, fingerprint: .}\*(Aq message +.fam +.fi +.if n .RE +.sp +Check get all ids in all idlists +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d negentropy \*(Aq.bounds | map(select(.mode == "idlist") | .idlist | .ids) | flatten\*(Aq message +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +fiatjaf, \c +.URL "https://fiatjaf.com" "" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/hoytech/negentropy" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBnes\fP\fP +.RS 4 +iNES/NES 2.0 cartridge ROM format +.sp +Limitations +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRprg_rom\fP, \f(CRchr_rom\fP and \f(CRtrainer\fP fields may contain data that is just random junk from the memory chips, since they are of a fixed size. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +The \f(CRnes_toasm\fP function outputs ALL opcodes, including the unofficial ones, which means that none of the regular assemblers can recompile it. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +The \f(CRnes_tokitty\fP function works on tiles in \f(CRchr_rom\fP but only outputs a Kitty graphics compatible string. You need to manually \f(CRprintf\fP that string to get Kitty (or another compatible terminal) to output the graphics. +.RE +.RE +.sp +Decompile PRG ROM +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-r \*(Aq.prg_rom[] | nes_toasm\*(Aq file.nes +.fam +.fi +.if n .RE +.sp +Print out first CHR ROM tile in Kitty (or Konsole, wayst, WezTerm) at size 5 +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ printf $(fq \-r \-d nes \*(Aq.chr_rom[0] | nes_tokitty(5)\*(Aq file.nes) +.fam +.fi +.if n .RE +.sp +Print out all CHR ROM tiles in Kitty (with Bash) at size 5 +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ for line in $(fq \-r \*(Aq.chr_rom[] | nes_tokitty(5)\*(Aq file.nes);do printf "%b%s" "$line";done +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Mikael Lofjärd \c +.MTO "mikael.lofjard\(atgmail.com" "" "," +original author +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.nesdev.org/wiki/INES" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.nesdev.org/wiki/NES_2.0" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.nesdev.org/wiki/CPU" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://bugzmanov.github.io/nes_ebook/chapter_6_3.html" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBogg\fP\fP +.RS 4 +OGG file +.RE +.sp +\f(CR\fBogg_page\fP\fP +.RS 4 +OGG page +.RE +.sp +\f(CR\fBopentimestamps\fP\fP +.RS 4 +OpenTimestamps file +.sp +View a full OpenTimestamps file +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq dd file.ots +.fam +.fi +.if n .RE +.sp +List the names of the Calendar servers used +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aq.operations | map(select(.attestation_type == "calendar") | .url)\*(Aq file.ots +.fam +.fi +.if n .RE +.sp +Check if there are Bitcoin attestations present +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aq.operations | map(select(.attestation_type == "bitcoin")) | length > 0\*(Aq file.ots +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +fiatjaf, \c +.URL "https://fiatjaf.com" "" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://opentimestamps.org/" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/opentimestamps/python\-opentimestamps" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBopus_packet\fP\fP +.RS 4 +Opus packet +.RE +.sp +\f(CR\fBpcap\fP\fP +.RS 4 +PCAP packet capture +.sp +Build object with number of (reassembled) TCP bytes sent to/from client IP +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +# for a pcapng file you would use .[0].tcp_connections for first section +$ fq \*(Aq.tcp_connections | group_by(.client.ip) | map({key: .[0].client.ip, value: map(.client.stream, .server.stream | tobytes.size) | add}) | from_entries\*(Aq +{ + "10.1.0.22": 15116, + "10.99.12.136": 234, + "10.99.12.150": 218 +} +.fam +.fi +.if n .RE +.RE +.sp +\f(CR\fBpcapng\fP\fP +.RS 4 +PCAPNG packet capture +.RE +.sp +\f(CR\fBpg_btree\fP\fP +.RS 4 +PostgreSQL btree index file +.sp +Btree index meta page +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d pg_btree \-o flavour=postgres14 ".[0] | d" 16404 +.fam +.fi +.if n .RE +.sp +Btree index page +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d pg_btree \-o flavour=postgres14 ".[1]" 16404 +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Pavel Safonov \c +.MTO "p.n.safonov\(atgmail.com" "" " " +.URL "https://github.com/pnsafonov" "@pnsafonov" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.postgresql.org/docs/current/storage\-page\-layout.html" "" "" +.RE +.RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o page=0\fP\fP +.RS 4 +First page number in file, default is 0 +.RE +.RE +.sp +\f(CR\fBpg_control\fP\fP +.RS 4 +PostgreSQL control file +.sp +Decode content of pg_control file +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d pg_control \-o flavour=postgres14 d pg_control +.fam +.fi +.if n .RE +.sp +Specific fields can be got by request +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d pg_control \-o flavour=postgres14 ".state, .check_point_copy.redo, .wal_level" pg_control +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Pavel Safonov \c +.MTO "p.n.safonov\(atgmail.com" "" " " +.URL "https://github.com/pnsafonov" "@pnsafonov" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://github.com/postgres/postgres/blob/REL_14_2/src/include/catalog/pg_control.h" "" "" +.RE +.RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o flavour=""\fP\fP +.RS 4 +PostgreSQL flavour: postgres14, pgproee14, postgres10 +.RE +.RE +.sp +\f(CR\fBpg_heap\fP\fP +.RS 4 +PostgreSQL heap file +.sp +To see heap page\(cqs content +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d pg_heap \-o flavour=postgres14 ".[0]" 16994 +.fam +.fi +.if n .RE +.sp +To see page\(cqs header +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d pg_heap \-o flavour=postgres14 ".[0].page_header" 16994 +.fam +.fi +.if n .RE +.sp +First and last item pointers on first page +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d pg_heap \-o flavour=postgres14 ".[0].pd_linp[0, \-1]" 16994 +.fam +.fi +.if n .RE +.sp +First and last tuple on first page +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d pg_heap \-o flavour=postgres14 ".[0].tuples[0, \-1]" 16994 +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Pavel Safonov \c +.MTO "p.n.safonov\(atgmail.com" "" " " +.URL "https://github.com/pnsafonov" "@pnsafonov" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.postgresql.org/docs/current/storage\-page\-layout.html" "" "" +.RE +.RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o flavour="postgres14"\fP\fP +.RS 4 +PostgreSQL flavour: postgres14, pgproee14, postgres10 +.RE +.sp +\f(CR\fB\-o page=0\fP\fP +.RS 4 +First page number in file, default is 0 +.RE +.sp +\f(CR\fB\-o segment=0\fP\fP +.RS 4 +Segment file number (16790.1 is 1), default is 0 +.RE +.RE +.sp +\f(CR\fBpng\fP\fP +.RS 4 +Portable Network Graphics file +.RE +.sp +\f(CR\fBprores_frame\fP\fP +.RS 4 +Apple ProRes frame +.RE +.sp +\f(CR\fBprotobuf\fP\fP +.RS 4 +Protobuf +.sp +Can decode sub messages +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \-d protobuf \*(Aq.fields[6].wire_value | protobuf | d\*(Aq file +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://developers.google.com/protocol\-buffers/docs/encoding" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBprotobuf_widevine\fP\fP +.RS 4 +Widevine protobuf +.RE +.sp +\f(CR\fBpssh_playready\fP\fP +.RS 4 +PlayReady PSSH +.RE +.sp +\f(CR\fBrtmp\fP\fP +.RS 4 +Real\-Time Messaging Protocol +.sp +Currently only supports plain RTMP (not RTMPT or encrypted variants etc) with AMF0 (not AMF3). +.sp +Show rtmp streams in PCAP file +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +fq \*(Aq.tcp_connections[] | select(.server.port=="rtmp") | d\*(Aq file.cap +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://rtmp.veriskope.com/docs/spec/" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://rtmp.veriskope.com/pdf/video_file_format_spec_v10.pdf" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBsafetensors\fP\fP +.RS 4 +SafeTensors +.RE +.sp +\f(CR\fBsll2_packet\fP\fP +.RS 4 +Linux cooked capture encapsulation v2 +.RE +.sp +\f(CR\fBsll_packet\fP\fP +.RS 4 +Linux cooked capture encapsulation +.RE +.sp +\f(CR\fBstl\fP\fP +.RS 4 +Stereolithography +.sp +Decode binary STL (Stereolithography, Standard Tesselation Language) files. +.sp +Current limitations +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +No support for ASCII STL files +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +No support for VisCAM and SolidView colors +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +No support for Materialise Magics colors +.RE +.RE +.RE +.sp +\f(CR\fBtap\fP\fP +.RS 4 +TAP tape format for ZX Spectrum computers +.sp +The TAP\- (and BLK\-) format is nearly a direct copy of the data that is stored in real tapes, as it is written by the ROM save routine of the ZX\-Spectrum. A TAP file is simply one data block or a group of 2 or more data blocks, one followed after the other. The TAP file may be empty. +You will often find this format embedded inside the TZX tape format. +The default file extension is \f(CR.tap\fP. +.sp +Processing JSON files +.RS 4 +When needing to process a generated JSON file it\(cqs recommended to convert the plain data bytes to an array by setting \f(CRbits_format=byte_array\fP: +.RE +.sp +.if n .RS 4 +.nf +.fam C +fq \-o bits_format=byte_array \-d tap \-V d /path/to/file.tap +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Michael R. Cook \c +.MTO "work.mrc\(atpm.me" "" "," +original author +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://worldofspectrum.net/zx\-modules/fileformats/tapformat.html" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBtar\fP\fP +.RS 4 +Tar archive +.RE +.sp +\f(CR\fBtcp_segment\fP\fP +.RS 4 +Transmission control protocol segment +.RE +.sp +\f(CR\fBtiff\fP\fP +.RS 4 +Tag Image File Format +.RE +.sp +\f(CR\fBtls\fP\fP +.RS 4 +Transport layer security +.sp +Supports decoding of most standard records, messages and extensions. Can also decrypt most standard cipher suites in a PCAP with traffic in both directions if a NSS key log is provided. +.sp +Decode and decrypt providing a PCAP and key log +.RS 4 +Write traffic to a PCAP file: +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ tcpdump \-i \-w traffic.pcap +.fam +.fi +.if n .RE +.sp +Make sure your curl TLS backend supports \f(CRSSLKEYLOGFILE\fP and do: +.sp +.if n .RS 4 +.nf +.fam C +$ SSLKEYLOGFILE=traffic.keylog curl \-\-tls\-max 1.2 https://host/path +.fam +.fi +.if n .RE +.sp +Decode, decrypt and query. Uses \f(CRkeylog=@\fP to read option value from keylog file: +.sp +.if n .RS 4 +.nf +.fam C +# decode and show whole tree +$ fq \-o keylog=@traffic.keylog d traffic.pcap + +# write unencrypted server response to a file. +# first .stream is the TCP stream, second .stream is TLS application data stream +# +# first TCP connections: +$ fq \-o keylog=@traffic.keylog \*(Aq.tcp_connections[0].server.stream.stream | tobytes\*(Aq traffic.pcap > data +# first TLS connection: +$ fq \-o keylog=@traffic.keylog\& \*(Aqfirst(grep_by(.server.stream | format == "tls")).server.stream.stream | tobytes\*(Aq > data +.fam +.fi +.if n .RE +.sp +Supported cipher suites for decryption +.RS 4 +\f(CRTLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA\fP, \f(CRTLS_DH_ANON_EXPORT_WITH_RC4_40_MD5\fP, \f(CRTLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA\fP, \f(CRTLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA\fP, \f(CRTLS_DHE_DSS_WITH_AES_128_CBC_SHA\fP, \f(CRTLS_DHE_DSS_WITH_AES_128_CBC_SHA256\fP, \f(CRTLS_DHE_DSS_WITH_AES_128_GCM_SHA256\fP, \f(CRTLS_DHE_DSS_WITH_AES_256_CBC_SHA\fP, \f(CRTLS_DHE_DSS_WITH_AES_256_CBC_SHA256\fP, \f(CRTLS_DHE_DSS_WITH_AES_256_GCM_SHA384\fP, \f(CRTLS_DHE_DSS_WITH_DES_CBC_SHA\fP, \f(CRTLS_DHE_DSS_WITH_RC4_128_SHA\fP, \f(CRTLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA\fP, \f(CRTLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA\fP, \f(CRTLS_DHE_RSA_WITH_AES_128_CBC_SHA\fP, \f(CRTLS_DHE_RSA_WITH_AES_128_CBC_SHA256\fP, \f(CRTLS_DHE_RSA_WITH_AES_128_GCM_SHA256\fP, \f(CRTLS_DHE_RSA_WITH_AES_256_CBC_SHA\fP, \f(CRTLS_DHE_RSA_WITH_AES_256_CBC_SHA256\fP, \f(CRTLS_DHE_RSA_WITH_AES_256_GCM_SHA384\fP, \f(CRTLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256\fP, \f(CRTLS_DHE_RSA_WITH_DES_CBC_SHA\fP, \f(CRTLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA\fP, \f(CRTLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA\fP, \f(CRTLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256\fP, \f(CRTLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256\fP, \f(CRTLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA\fP, \f(CRTLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384\fP, \f(CRTLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384\fP, \f(CRTLS_ECDH_ECDSA_WITH_RC4_128_SHA\fP, \f(CRTLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA\fP, \f(CRTLS_ECDH_RSA_WITH_AES_128_CBC_SHA\fP, \f(CRTLS_ECDH_RSA_WITH_AES_128_CBC_SHA256\fP, \f(CRTLS_ECDH_RSA_WITH_AES_128_GCM_SHA256\fP, \f(CRTLS_ECDH_RSA_WITH_AES_256_CBC_SHA\fP, \f(CRTLS_ECDH_RSA_WITH_AES_256_CBC_SHA384\fP, \f(CRTLS_ECDH_RSA_WITH_AES_256_GCM_SHA384\fP, \f(CRTLS_ECDH_RSA_WITH_RC4_128_SHA\fP, \f(CRTLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA\fP, \f(CRTLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA\fP, \f(CRTLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256\fP, \f(CRTLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\fP, \f(CRTLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA\fP, \f(CRTLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384\fP, \f(CRTLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\fP, \f(CRTLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\fP, \f(CRTLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305\fP, \f(CRTLS_ECDHE_ECDSA_WITH_RC4_128_SHA\fP, \f(CRTLS_ECDHE_PSK_WITH_AES_128_CBC_SHA\fP, \f(CRTLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256\fP, \f(CRTLS_ECDHE_PSK_WITH_AES_256_CBC_SHA\fP, \f(CRTLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA\fP, \f(CRTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA\fP, \f(CRTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256\fP, \f(CRTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\fP, \f(CRTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\fP, \f(CRTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384\fP, \f(CRTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\fP, \f(CRTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\fP, \f(CRTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305\fP, \f(CRTLS_ECDHE_RSA_WITH_RC4_128_SHA\fP, \f(CRTLS_PSK_WITH_AES_128_CBC_SHA\fP, \f(CRTLS_PSK_WITH_AES_256_CBC_SHA\fP, \f(CRTLS_PSK_WITH_RC4_128_SHA\fP, \f(CRTLS_RSA_EXPORT_WITH_DES40_CBC_SHA\fP, \f(CRTLS_RSA_EXPORT_WITH_RC4_40_MD5\fP, \f(CRTLS_RSA_WITH_3DES_EDE_CBC_SHA\fP, \f(CRTLS_RSA_WITH_AES_128_CBC_SHA\fP, \f(CRTLS_RSA_WITH_AES_128_CBC_SHA256\fP, \f(CRTLS_RSA_WITH_AES_128_GCM_SHA256\fP, \f(CRTLS_RSA_WITH_AES_256_CBC_SHA\fP, \f(CRTLS_RSA_WITH_AES_256_CBC_SHA256\fP, \f(CRTLS_RSA_WITH_AES_256_GCM_SHA384\fP, \f(CRTLS_RSA_WITH_DES_CBC_SHA\fP, \f(CRTLS_RSA_WITH_RC4_128_MD5\fP, \f(CRTLS_RSA_WITH_RC4_128_SHA\fP +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.rfc\-editor.org/rfc/rfc5246" "RFC 5246: The Transport Layer Security (TLS) Protocol" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.rfc\-editor.org/rfc/rfc6101" "RFC 6101: The Secure Sockets Layer (SSL) Protocol Version 3.0" "" +.RE +.RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o keylog=""\fP\fP +.RS 4 +NSS Key Log content +.RE +.RE +.sp +\f(CR\fBtoml\fP\fP +.RS 4 +Tom\(cqs Obvious, Minimal Language +.RE +.sp +\f(CR\fBtzif\fP\fP +.RS 4 +Time Zone Information Format +.sp +Get last transition time +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +fq \*(Aq.v2plusdatablock.transition_times[\-1] | tovalue\*(Aq tziffile +.fam +.fi +.if n .RE +.sp +Count leap second records +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +fq \*(Aq.v2plusdatablock.leap_second_records | length\*(Aq tziffile +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Takashi Oguma \c +.URL "https://github.com/bitbears\-dev" "@bitbears\-dev" " " +.URL "https://twitter.com/0xb17bea125" "@0xb17bea125" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://datatracker.ietf.org/doc/html/rfc8536" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBtzx\fP\fP +.RS 4 +TZX tape format for ZX Spectrum computers +.sp +\f(CRTZX\fP is a file format designed to preserve cassette tapes compatible with the ZX Spectrum computers, although some specialized versions of the format have been defined for other machines such as the Amstrad CPC and C64. +The format was originally created by Tomaz Kac, who was the maintainer until \f(CRrevision 1.13\fP, before passing it to Martijn v.d. Heide. For a brief period the company Ramsoft became the maintainers, and created revision \f(CRv1.20\fP. +The default file extension is \f(CR.tzx\fP. +.sp +Processing JSON files +.RS 4 +When needing to process a generated JSON file it\(cqs recommended to convert the plain data bytes to an array by setting \f(CRbits_format=byte_array\fP: +.RE +.sp +.if n .RS 4 +.nf +.fam C +fq \-o bits_format=byte_array \-d tzx \-V d /path/to/file.tzx +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Michael R. Cook \c +.MTO "work.mrc\(atpm.me" "" "," +original author +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://worldofspectrum.net/TZXformat.html" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBudp_datagram\fP\fP +.RS 4 +User datagram protocol +.RE +.sp +\f(CR\fBvorbis_comment\fP\fP +.RS 4 +Vorbis comment +.RE +.sp +\f(CR\fBvorbis_packet\fP\fP +.RS 4 +Vorbis packet +.RE +.sp +\f(CR\fBvp8_frame\fP\fP +.RS 4 +VP8 frame +.RE +.sp +\f(CR\fBvp9_cfm\fP\fP +.RS 4 +VP9 Codec Feature Metadata +.RE +.sp +\f(CR\fBvp9_frame\fP\fP +.RS 4 +VP9 frame +.RE +.sp +\f(CR\fBvpx_ccr\fP\fP +.RS 4 +VPX Codec Configuration Record +.RE +.sp +\f(CR\fBwasm\fP\fP +.RS 4 +WebAssembly Binary Format +.sp +Count opcode usage +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aq.sections[] | select(.id == "code_section") | [.. | .opcode? // empty] | count | map({key: .[0], value: .[1]}) | from_entries\*(Aq file.wasm +.fam +.fi +.if n .RE +.sp +List exports and imports +.RS 4 +.RE +.sp +.if n .RS 4 +.nf +.fam C +$ fq \*(Aq.sections | {import: map(select(.id == "import_section").content.im.x[].nm.b), export: map(select(.id == "export_section").content.ex.x[].nm.b)}\*(Aq file.wasm +.fam +.fi +.if n .RE +.sp +Authors +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Takashi Oguma \c +.URL "https://github.com/bitbears\-dev" "@bitbears\-dev" " " +.URL "https://twitter.com/0xb17bea125" "@0xb17bea125" "" +.RE +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://webassembly.github.io/spec/core/" "" "" +.RE +.RE +.RE +.sp +\f(CR\fBwav\fP\fP +.RS 4 +WAV file +.RE +.sp +\f(CR\fBwebp\fP\fP +.RS 4 +WebP image +.RE +.sp +\f(CR\fBxml\fP\fP +.RS 4 +Extensible Markup Language +.sp +XML can be decoded and encoded into jq values in two ways, elements as object or array. The object variant might be easier to query for a specific value but array might be easier to use to generate xml or to query after all elements of some kind etc. +Encoding is done using the \f(CRto_xml\fP function and it will figure what variant that is used based on the input value. It has two optional options \f(CRindent\fP and \f(CRattribute_prefix\fP. +.sp +Elements as object +.RS 4 +Element can have different shapes depending on body text, attributes and children: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtext\fP is \f(CR{"a":{"#text":"text","@key":"value"}}\fP, has text (\f(CR#text\fP) and attributes (\f(CR@key\fP) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtext\fP is \f(CR{"a":"text"}\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtext\fP is \f(CR{"a":{"b":"text"}}\fP one child with only text and no attributes +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtext\fP is \f(CR{"a":{"b":["","text"]}}\fP two children with same name end up in an array +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtext\fP is \f(CR{"a":{"b":["",{"#text":"text","@key":"value"}]}}\fP +.RE +.RE +.sp +If there is \f(CR#seq\fP attribute it encodes the child element order. Use \f(CR\-o seq=true\fP to include sequence number when decoding, otherwise order might be lost. +.sp +.if n .RS 4 +.nf +.fam C +# decode as object is the default +$ echo \*(Aqbbbccc\*(Aq | fq \-d xml \-o seq=true +{ + "a": { + "b": [ + { + "#seq": 0 + }, + { + "#seq": 1, + "#text": "bbb" + } + ], + "c": { + "#seq": 2, + "#text": "ccc", + "@attr": "value" + } + } +} + +# access text of the element +$ echo \*(Aqbbbccc\*(Aq | fq \*(Aq.a.c["#text"]\*(Aq +"ccc" + +# decode to object and encode to xml +$ echo \*(Aqbbbccc\*(Aq | fq \-r \-d xml \-o seq=true \*(Aqto_xml({indent:2})\*(Aq + + + bbb + ccc + +.fam +.fi +.if n .RE +.sp +Elements as array +.RS 4 +Elements are arrays of the shape \f(CR["name", null|{#text|attribute: "value"}, [, .\|.\|.]]\fP. +.RE +.sp +.if n .RS 4 +.nf +.fam C +# decode as array +$ echo \*(Aqbbbccc\*(Aq | fq \-d xml \-o array=true +[ + "a", + null, + [ + [ + "b", + null, + [] + ], + [ + "b", + { + "#text": "bbb" + }, + [] + ], + [ + "c", + { + "#text": "ccc", + "attr": "value" + }, + [] + ] + ] +] + +# decode to array and encode to xml +$ echo \*(Aqbbbccc\*(Aq | fq \-r \-d xml \-o array=true \-o seq=true \*(Aqto_xml({indent:2})\*(Aq + + + bbb + ccc + + +# access text of the element, the object variant above is probably easier to use +$ echo \*(Aqbbbccc\*(Aq | fq \-o array=true \*(Aq.[2][2][1]["#text"]\*(Aq +"ccc" +.fam +.fi +.if n .RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://www.xml.com/pub/a/2006/05/31/converting\-between\-xml\-and\-json.html" "xml.com\(cqs Converting Between XML and JSON" "" +.RE +.RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o array=false\fP\fP +.RS 4 +Decode as nested arrays +.RE +.sp +\f(CR\fB\-o attribute_prefix="@"\fP\fP +.RS 4 +Prefix for attribute keys +.RE +.sp +\f(CR\fB\-o seq=false\fP\fP +.RS 4 +Use seq attribute to preserve element order +.RE +.RE +.sp +\f(CR\fByaml\fP\fP +.RS 4 +YAML Ain\(cqt Markup Language +.RE +.sp +\f(CR\fBzip\fP\fP +.RS 4 +ZIP archive +.sp +Supports ZIP64. +.sp +Timestamp and time zones +.RS 4 +The timestamp accessed via \f(CR.local_files[].last_modification\fP is encoded in ZIP files using \c +.URL "https://learn.microsoft.com/en\-us/windows/win32/api/oleauto/nf\-oleauto\-dosdatetimetovarianttime" "MS\-DOS representation" "" +which lacks a known time zone. Probably the local time/date was used at creation. The \f(CRunix_guess\fP field in \f(CRlast_modification\fP is a guess assuming the local time zone was UTC at creation. +.RE +.sp +References +.RS 4 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://opensource.apple.com/source/zip/zip\-6/unzip/unzip/proginfo/extra.fld" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://formats.kaitai.io/dos_datetime/" "" "" +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +.URL "https://learn.microsoft.com/en\-us/windows/win32/api/oleauto/nf\-oleauto\-dosdatetimetovarianttime" "" "" +.RE +.RE +.sp +.B Options +.br +.sp +\f(CR\fB\-o uncompress=true\fP\fP +.RS 4 +Uncompress and probe files +.RE +.RE +.SH "ENCODINGS, SERIALIZATIONS AND HASHES" +.sp +In addition to binary formats fq also supports various encodings and serialization formats. +.sp +At the moment fq does not have any dedicated argument for serialization formats but raw string input \f(CR\-R\fP slurp \f(CR\-s\fP and raw string output \f(CR\-r\fP can make things easier. The combination \f(CR\-Rs\fP will read all inputs into one string (same as jq). +.sp +Note that \f(CRfrom*\fP functions output jq values and \f(CRto*\fP functions take jq values as input so in some cases not all information will be properly preserved. For example, the element and attribute order might change and text and comment nodes might move or be merged. \c +.URL "https://github.com/mikefarah/yq" "yq" "" +might be a better tool if that is needed. +.sp +Some example usages: +.sp +.if n .RS 4 +.nf +.fam C +# read yml (format is probed, use \-d yaml to force) and do some query +$ fq \*(Aq...\*(Aq file.yml + +# convert YAML to JSON +# note the \-r for raw string output, without it a JSON string with escaped JSON would be output +$ fq \-r \*(Aqtojson({indent:2})\*(Aq file.yml + +# add token to URL +$ echo \-n "https://host.org" | fq \-Rsr \*(Aqfrom_url | .user.username="token" | to_url\*(Aq +https://token@host.org + +# top 3 hosts in src or href attributes: +# \-d to decode as html, can\*(Aqt be probed as html5 parsers always produce some parse tree +# [...] to start collect values into an array +# .. | ."@src"?, ."@href"? | values, recurse and try (?) to get src and href attributes and filter out nulls +# from_url.host | values, parse as url and filter out those without a host +# count to count unique values, returns [[key, count], ...] +# reverse sort by count and pick first 3 +# map [key, count] tuples into {key: key, value: count} +# from_entries, convert into object +$ curl \-s https://www.discogs.com/ | fq \-d html \*(Aq[.. | ."@src"?, ."@href"? | values | from_url.host | values] | count | sort_by(\-.[1])[0:3] | map({key: .[0], value: .[1]}) | from_entries\*(Aq +{ + "blog.discogs.com": 9, + "st.discogs.com": 10, + "www.discogs.com": 14 +} + +# shows how serialization functions can be used on any string, how to transform values and output some other format +# read and decode zip file and start an interactive REPL +$ fq\& \-i . <(curl \-sL https://github.com/stefangabos/world_countries/archive/master.zip) +# select from interesting xml file +zip> .local_files[] | select(.file_name == "world_countries\-master/data/countries/en/world.xml").uncompressed | repl +# convert xml into jq value +> .local_files[95].uncompressed string> from_xml | repl +# sort countries by and select the first one +>> object> .countries.country | sort_by(."@name") | first | repl +# see what current input is +>>> object> . +{ + "@alpha2": "af", + "@alpha3": "afg", + "@id": "4", + "@name": "Afghanistan" +} +# remove "@" prefix from keys and convert to YAML and print it +>>> object> with_entries(.key |= .[1:]) | to_yaml | print +alpha2: af +alpha3: afg +id: "4" +name: Afghanistan +# exit all REPLs back to shell +>>> object> ^D +>> object> ^D +> .local_files[95].uncompressed string> ^D +zip> ^D +.fam +.fi +.if n .RE +.SS "XML and HTML" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_xml\fP/\f(CRfrom_xml($opts)\fP Parse XML into jq value. \f(CR$opts\fP are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{seq: true}\fP preserve element ordering if more than one sibling. +.br +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{array: true}\fP use nested \f(CR[name, attributes, children]\fP arrays to represent elements. Attributes will be \f(CRnull\fP if none and children will be \f(CR[]\fP if none, this is to make it easier to work with as the array always has 3 values. \f(CRto_xml\fP does not require this. +.br +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_html\fP/\f(CRfrom_html($opts)\fP Parse HTML into jq value. +.br +Similar to \f(CRfrom_xml\fP but parses html5 in non\-script mode. Will always have a \f(CRhtml\fP root with \f(CRhead\fP and \f(CRbody\fP elements. ++ +\f(CR$opts\fP are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{array: true}\fP use nested arrays to represent elements. +.br +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{seq: true}\fP preserve element ordering if more than one sibling. +.br +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_xml\fP/\f(CRto_xml($opts)\fP Serialize jq value into XML. +.br +Assumes object representation if input is an object, and nested arrays if input is an array. +.br +Will automatically add a root \f(CRdoc\fP element if jq value has more than one root element. +.br +If a \f(CR#seq\fP is found on at least one element all siblings will be sorted by sequence number. Attributes are always sorted. ++ +\f(CR$opts\fP are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{indent: number}\fP indent child elements. +.RE +.RE +.sp +XML elements can be represented as jq value in two ways, as objects (inspired by \c +.URL "https://github.com/clbanning/mxj" "mxj" "" +and +.URL "https://www.xml.com/pub/a/2006/05/31/converting\-between\-xml\-and\-json.html" "xml.com\(cqs Converting Between XML and JSON" ")" +or nested arrays. Both representations are lossy and might lose ordering of elements, text nodes and comments. In object representation \f(CRfrom_xml\fP, \f(CRfrom_html\fP and \f(CRto_xml\fP support \f(CR{seq: true}\fP option to parse/serialize \f(CR{"#seq": }\fP attributes to preserve element sibling ordering. +.sp +The object version is denser and convenient to query, the nested arrays version is probably easier to use when generating XML. +.sp +Let\(cqs assume \f(CR$xml\fP is this XML document as a string: +.sp +.if n .RS 4 +.nf +.fam C + + + text + text + +.fam +.fi +.if n .RE +.sp +With object representation an element is represented as: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Attributes as \f(CR@\fP prefixed \f(CR@\fP keys. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Text nodes as \f(CR#text\fP. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Comment nodes as \f(CR#comment\fP keys. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +For explicit sibling ordering \f(CR#seq\fP keys with a number, can be negative, assumed zero if missing. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Child element with only text as \f(CR\fP key with text as value. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Child element with more than just text as \f(CR\fP key with value an object. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Multiple child element siblings with same name as \f(CR\fP key with value as array with strings and objects. +.RE +.sp +.if n .RS 4 +.nf +.fam C +> $xml | from_xml +{ + "doc": { + "child": [ + { + "@attr": "1" + }, + { + "#text": "text", + "@attr": "2" + } + ], + "other": "text" + } +} +.fam +.fi +.if n .RE +.sp +With nested array representation, an array with these values \f(CR["", {attributes.\|.\|.}, [children.\|.\|.]]\fP. +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Index 0 is an element name. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Index 1 object attributes (including \f(CR#text\fP and \f(CR#comment\fP keys). +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Index 2 array of child elements. +.RE +.sp +.if n .RS 4 +.nf +.fam C +> $xml | from_xml({array: true}) +[ + "doc", + null, + [ + [ + "child", + { + "attr": "1" + }, + [] + ], + [ + "child", + { + "#text": "text", + "attr": "2" + }, + [] + ], + [ + "other", + { + "#text": "text" + }, + [] + ] + ] +] +.fam +.fi +.if n .RE +.sp +Parse and include \f(CR#seq\fP attributes if needed: +.sp +.if n .RS 4 +.nf +.fam C +> $xml | from_xml({seq:true}) +{ + "doc": { + "child": [ + { + "#seq": 0, + "@attr": "1" + }, + { + "#seq": 1, + "#text": "text", + "@attr": "2" + } + ], + "other": { + "#seq": 2, + "#text": "text" + } + } +} +.fam +.fi +.if n .RE +.sp +Select values in \f(CR\fP, remove \f(CR\fP, add a \f(CR\fP element, serialize to xml with 2 space indent and print the string +.sp +.if n .RS 4 +.nf +.fam C +> $xml | from_xml.doc | del(.child) | .new = "abc" | {root: .} | to_xml({indent: 2}) | println + + abc + text + +.fam +.fi +.if n .RE +.SS "JSON" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfromjson\fP Parse JSON into jq value. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRtojson\fP/\f(CRtojson($opts)\fP Serialize jq value into JSON. \f(CR$opts\fP are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{indent: number}\fP Indent depth. +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_jsonl\fP Parse JSON lines into jq array. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_jsonl\fP Serialize jq array into JSONL. +.RE +.SS "jq\-flavoured JSON" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_jq\fP Parse jq\-flavoured JSON into jq value. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_jq\fP/\f(CRto_jq($opts)\fP Serialize jq value into jq\-flavoured JSON. jq\-flavoured JSON has optional key quotes, \f(CR#\fP comments and can have trailing comma in objects. \f(CR$opts\fP are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{indent: number}\fP Indent depth. +.RE +.RE +.sp +Note that \f(CRfromjson\fP and \f(CRtojson\fP use different naming conventions as they originate from jq\(cqs standard library. +.SS "YAML" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_yaml\fP Parse YAML into jq value. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_yaml\fP/\f(CRto_yaml($opts)\fP Serialize jq value into YAML. \f(CR$opts\fP are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{indent: number}\fP Indent depth. +.RE +.RE +.SS "TOML" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_toml\fP Parse TOML into jq value. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_toml\fP/\f(CRto_toml($opts)\fP Serialize jq value into TOML. \f(CR$opts\fP are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{indent: number}\fP Indent depth. +.RE +.RE +.SS "CSV" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_csv\fP/\f(CRfrom_csv($opts)\fP Parse CSV into jq value. +.br +To work with tab separated values you can use \f(CRfrom_csv({comma: "\(rst"})\fP or \f(CRfq \-d csv \-o \*(Aqcomma="\(rst"\*(Aq\fP. +.br +\f(CR$opts\fP are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{comma: string}\fP field separator, default ",". +.br +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{comment: string}\fP comment line character, default "#". +.br +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_csv\fP/\f(CRto_csv($opts)\fP Serialize jq value into CSV. \f(CR$opts\fP are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{comma: string}\fP field separator, default ",". +.br +.RE +.RE +.SS "XML entities" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_xmlentities\fP Decode XML entities. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_xmlentities\fP Encode XML entities. +.RE +.SS "URL" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_urlpath\fP Decode URL path component. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_urlpath\fP Encode URL path component. Whitespace as %20. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_urlencode\fP Decode URL query encoding. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_urlencode\fP Encode URL to query encoding. Whitespace as "+". +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_urlquery\fP Decode URL query into object. For duplicate keys value will be an array. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_urlquery\fP Encode object into query string. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_url\fP Decode URL into object. +.sp +.if n .RS 4 +.nf +.fam C +> "schema://user:pass@host/path?key=value#fragment" | from_url +{ + "fragment": "fragment", + "host": "host", + "path": "/path", + "query": { + "key": "value" + }, + "rawquery": "key=value", + "scheme": "schema", + "user": { + "password": "pass", + "username": "user" + } +} +.fam +.fi +.if n .RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_url\fP Encode object into URL string. +.RE +.SS "Hex and base64" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_hex\fP Decode hex string to binary. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_hex\fP Encode binary into hex string. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_base64\fP/\f(CRfrom_base64($opts)\fP Decode base64 encodings into binary. \f(CR$opts\fP are: +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{encoding:string}\fP encoding variant: \f(CRstd\fP (default), \f(CRurl\fP, \f(CRrawstd\fP or \f(CRrawurl\fP +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_base64\fP/\f(CRto_base64($opts)\fP Encode binary into base64 encodings. \f(CR$opts\fP are: +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CR{encoding:string}\fP encoding variant: \f(CRstd\fP (default), \f(CRurl\fP, \f(CRrawstd\fP or \f(CRrawurl\fP +.RE +.SS "Hash functions" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_md4\fP Hash binary using md4. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_md5\fP Hash binary using md5. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_sha1\fP Hash binary using sha1. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_sha256\fP Hash binary using sha256. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_sha512\fP Hash binary using sha512. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_sha3_224\fP Hash binary using sha3 224. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_sha3_256\fP Hash binary using sha3 256. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_sha3_384\fP Hash binary using sha3 384. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_sha3_512\fP Hash binary using sha3 512. +.RE +.SS "Text encodings" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_iso8859_1\fP Encode string as ISO8859\-1 into binary. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_iso8859_1\fP Decode binary as ISO8859\-1 into string. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_utf8\fP Encode string as UTF8 into binary. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_utf8\fP Decode binary as UTF8 into string. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_utf16\fP Encode string as UTF16 into binary. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_utf16\fP Decode binary as UTF16 into string. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_utf16le\fP Encode string as UTF16 little\-endian into binary. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_utf16le\fP Decode binary as UTF16 little\-endian into string. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRto_utf16be\fP Encode string as UTF16 big\-endian into binary. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +\f(CRfrom_utf16be\fP Decode binary as UTF16 big\-endian into string. +.RE +.SH "INTERACTIVE REPL" +.sp +The REPL can be useful in some scenarios: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +When decoding is slow you can reuse the decode result. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Dig thru a file using sub\-REPL to cut down on typing. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +. sp -1 +. IP \(bu 2.3 +.\} +Use auto\-completion to speed up typing. +.RE +.sp +.if n .RS 4 +.nf +.fam C +# start REPL with no (null) input +$ fq \-i +null> +# same as +$ fq \-ni +null> + +# in the REPL you will see a prompt indicating current input and you can type a jq expression to evaluate. + +# start REPL with one file as input +$ fq \-i . doc/file.mp3 +mp3> +# basic arithmetic and jq expressions +mp3> 1+1 +2 +mp3> 1, 2, 3 | . * 2 +2 +4 +6 +mp3> [1, 2, 3] | add +6 +# "." is the identity function which just returns current input, the mp3 file. +mp3> . +# access the first frame in the mp3 file +mp3> .frames[0] +# start a new nested REPL with first frame as input +mp3> .frames[0] | repl +# prompt shows "path" to current input and that it\*(Aqs an mp3_frame. +# Ctrl\-D to exit REPL or to shell if last REPL +> .frames[0] mp3_frame> ^D +# "jq" value of layer in first frame +mp3> .frames[0].header.layer | tovalue +3 +mp3> .frames[0].header.layer * 2 +6 +# symbolic value, same as "jq" value +mp3> .frames[0].header.layer | tosym +3 +# actual underlying decoded value +mp3> .frames[0].header.layer | toactual +1 +# description of value +mp3> .frames[0].header.layer | todescription +"MPEG Layer 3" +mp3> ^D +$ +.fam +.fi +.if n .RE +.sp +Use Ctrl\-D to exit and Ctrl\-C to interrupt current evaluation. +.SH "EXAMPLES" +.SS "Basic usage" +.sp +fq tries to behave the same way as jq as much as possible, so you can do: +.sp +.if n .RS 4 +.nf +.fam C +fq . file +fq < file +cat file | fq +fq . < file +fq . *.png *.mp3 +fq \*(Aq.frames[0]\*(Aq *.mp3 +fq \*(Aq.frames[\-1] | tobytes\*(Aq file.mp3 > last_frame +.fam +.fi +.if n .RE +.SS "Common usages" +.sp +.if n .RS 4 +.nf +.fam C +# recursively display decode tree but truncate long arrays +fq d file +# same as +fq display file + +# display all bytes for each value +fq dd file +# same as +fq \*(Aqd({array_truncate: 0, string_truncate: 0, display_bytes: 0})\*(Aq file + +# display 200 bytes for each value +fq \*(Aqd({display_bytes: 200})\*(Aq file + +# recursively display decode tree without truncating +fq da file +# same as +fq \*(Aqd({array_truncate: 0, string_truncate: 0})\*(Aq file + +# display a specific decode tree one level +fq \*(Aq.path[1].to.value\*(Aq file +# display a specific decode tree all levels +fq \*(Aq.path[1].to.value | d\*(Aq file +fq \*(Aq.path[1].to.value | dd\*(Aq file +fq \*(Aq.path[1].to.value | da\*(Aq file + +# recursively and verbosely display decode tree +fq dv file +# same as +fq \*(Aqd({array_truncate: 0, string_truncate: 0, verbose: true})\*(Aq file + +# JSON representation for whole file +fq tovalue file +# or use \-V (\-\-value\-output) that does tovalue automatically +fq \-V . file +# or \-Vr if the value is a string and you want a "raw" string +fq \-Vr .path.to.string file +# JSON but raw bit fields truncated +fq \-o bits_format=truncate tovalue file +# JSON but raw bit fields as md5 hex string +fq \-o bits_format=md5 tovalue file +# JSON but raw bit fields as byte arrays +fq \-o bits_format=byte_array tovalue file +# look up a path +fq \*(Aq.some[1].path\*(Aq file +# look up a path and output JSON +fq \-V \*(Aq.some[1].path\*(Aq file +# can be a query that outputs multiple values +# this outputs first and last value in .some array and .path, three values in total +fq \-V \*(Aq.some[0,\-1], .path\*(Aq file + +# grep whole tree by value +fq \*(Aqgrep("^prefix")\*(Aq file +fq \*(Aqgrep(123)\*(Aq file +# grep whole tree by condition +fq \*(Aqgrep_by(. >= 100 and . <= 100)\*(Aq file + +# recursively look for values fulfilling some condition +fq \*(Aq.. | select(.type=="trak")?\*(Aq file +fq \*(Aqgrep_by(.type=="trak")\*(Aq file +# grep_by(f) is an alias for .. | select(f)?, that is: recurse, select and ignore errors + +# recursively look for decode value roots for a format +fq \*(Aq.. | select(format=="jpeg")\*(Aq file +# can also use grep_by +fq \*(Aqgrep_by(format=="jpeg")\*(Aq file + +# recursively look for first decode value root for a format +fq \*(Aqfirst(.. | select(format=="jpeg"))\*(Aq file +fq \*(Aqfirst(grep_by(format=="jpeg"))\*(Aq file + +# decode file as mp4 and return a result even if there are some errors +fq \-d mp4 file.mp4 +# decode file as mp4 and also ignore validity assertions +fq \-o force=true \-d mp4 file.mp4 +.fam +.fi +.if n .RE +.SH "AUTHOR" +.sp +Mattias Wadman \ No newline at end of file diff --git a/doc/fq.1.adoc b/doc/fq.1.adoc new file mode 100644 index 000000000..659f89528 --- /dev/null +++ b/doc/fq.1.adoc @@ -0,0 +1,3227 @@ += fq(1) +Mattias Wadman +v0.17.0 +:doctype: manpage +:manmanual: FQ +:mansource: FQ +:toc: left +:toclevels: 3 +:sectanchors: +:reproducible: + +== Name + +fq - tool, language and decoders for working with binary formats + +== Synopsis + +`*fq [_OPTIONS_] [--] [_EXPRESSION_] [_FILE_...]*` + +== Description + +**fq** is a tool, language, and decoders for working with binary formats and data. +In most cases it behaves and feels similar to https://jqlang.github.io/jq/[jq] and it +also uses the same expression language. +To get the most out of fq it's recommended to learn more about jq. + +It features a structural hex viewer, nested format decoding, slicing and concatenating +binary data, bit-level decoding and an interactive REPL with auto-completion. + +[source,console] +---- +# Evaluate "d" for file.mp4 +$ fq d file.mp4 + +# Evaluate ".boxes[0].type" for all *.mp4 files +$ fq '.boxes[0].type' *.mp4 + +# Evaluate "1+2" without reading any input +$ fq -n 1+2 +---- + +For more advanced usage see `--arg`, `--slurp`, `--raw-file`, `-n` etc and examples at the end of the documentation. + +== Options + +`*--arg NAME VALUE*`:: + Set $NAME to string VALUE + +`*--argdecode NAME PATH*`:: + Set $NAME to decode of PATH + +`*--argjson NAME JSON*`:: + Set $NAME to JSON + +`*--args*`:: + Consume remaining arguments as positional strings + +`*--color-output*`, `*-C*`:: + Force color output + +`*--compact-output*`, `*-c*`:: + Use compact output + +`*--decode*`, `*-d NAME*`:: + Decode format or group (probe) + +`*--from-file*`, `*-f PATH*`:: + Read EXPRESSION from file + +`*--help*`, `*-h [TOPIC]*`:: + Show help for TOPIC (ex: -h formats, -h mp4) + +`*--include-path*`, `*-L PATH*`:: + Add PATH to include search paths + +`*--join-output*`, `*-j*`:: + No newline after each output + +`*--jsonargs*`:: + Consume remaining arguments as positional JSON + +`*--monochrome-output*`, `*-M*`:: + Force monochrome output + +`*--null-input*`, `*-n*`:: + Null input (use input and inputs to read) + +`*--option*`, `*-o NAME=VALUE/@PATH*`:: + Set option (ex: -o color=true, see --help options) +`*-o addrbase=number*`::: + Number base for addresses + +`*-o array_truncate=number*`::: + Array display length to truncate + +`*-o bits_format=string*`::: + Raw bits representation +`*-o bits_format=base64*`:::: + Base64 string. +`*-o bits_format=byte_array*`:::: + Array of bytes (zero bit padded if size is not byte aligned). +`*-o bits_format=hex*`:::: + Hex string. +`*-o bits_format=md5*`:::: + MD5 hex string (zero bit padded). +`*-o bits_format=snippet*`:::: + Truncated Base64 string prefixed with bit length. +`*-o bits_format=string*`:::: + String with raw bytes (zero bit padded if size is not byte aligned). The string is binary safe internally in fq but bytes not representable as UTF-8 will be lost if turned into JSON (default). +`*-o bits_format=truncate*`:::: + Truncated string. + +`*-o byte_colors=ranges=string,...*`::: + Byte value colorization + +`*-o color=true|false*`::: + Use color + +`*-o colors=key=value,...*`::: + Color scheme + +`*-o compact=true|false*`::: + Use compact JSON + +`*-o completion_timeout=number*`::: + Seconds to wait for completion results + +`*-o depth=number*`::: + Display tree depth limit + +`*-o display_bytes=number*`::: + Display bytes limit + +`*-o force=true|false*`::: + Force decode + +`*-o join_string=string*`::: + String used to join outputs + +`*-o line_bytes=number*`::: + Number of bytes per display line + +`*-o raw_string=true|false*`::: + Raw string output + +`*-o sizebase=number*`::: + Number base for sizes + +`*-o skip_gaps=true|false*`::: + Skip gaps when representing decode value (arrays) as JSON + +`*-o string_truncate=number*`::: + String display length truncate + +`*-o unicode=true|false*`::: + Use unicode + +`*-o verbose=true|false*`::: + Verbose display + +`*-o width=number*`::: + Terminal width + + +`*--raw-file NAME PATH*`:: + Set $NAME to string content of file + +`*--raw-input*`, `*-R*`:: + Read raw input strings (don't decode) + +`*--raw-output*`, `*-r*`:: + Raw string output (without quotes) + +`*--raw-output0*`:: + NUL (zero) byte after each output + +`*--repl*`, `*-i*`:: + Interactive REPL + +`*--slurp*`, `*-s*`:: + Slurp all inputs into an array or string (-Rs) + +`*--unicode-output*`, `*-U*`:: + Force unicode output + +`*--value-output*`, `*-V*`:: + Output JSON value (-Vr for raw string) + +`*--version*`, `*-v*`:: + Show version + + +== Configuration + +=== Init files + +To add your own functions you can use `init.jq` that is read from: + +macOS:: + `$HOME/Library/Application Support/fq/init.jq` or + + `$HOME/.config/fq/init.jq` +Linux and BSD:: + `$HOME/.config/fq/init.jq` +Windows:: + `%AppData%\fq\init.jq` + +=== Environment + +`*NO_COLOR*`:: + Non-empty string disables color output. +`*CLIUNICODE*`:: + Enables use of unicode output characters. +`*COMPLETION_TIMEOUT*`:: + REPL completion timeout in seconds. +`*NO_DECODE_PROGRESS*`:: + Disables decode progress indicator. + +== Expression + +=== Syntax + +See *jq*(1) for syntax details. But here are some common beginner gotchas: + +- Functions that take no arguments are called using `name` instead of `name()`. +- Arguments are separated by `;` instead of `,`. Comma is used to concatenate output streams. +To call a function `f` with two arguments use `f(1; 2)`. If you do `f(1, 2)` +you pass a single argument `1, 2`, a filter that outputs `1` and then `2`, to `f`. +- Expressions can return or "output" zero or more values. This is how iteration etc is done, `1, 2` outputs `1` then `2`. +- Similar to shell pipelines, implicit input and output are used and piped together using `|`. `.` is used to refer to +current input. Ex `1 | . + 2` outputs `3`, `1, 2 | . + 2` outputs `3` and `4`. +- In the jq manual and other jq related documentation you might see `name/2`, this means the +function `name` takes two arguments (arity). + +=== Additional features + +fq uses an extended variant of the jq language with a few extra features: + +* Arbitrary-precision integers and arithmetics. +* Supports raw strings using back-ticks. String interpolation and codepoint escapes are not processed. ++ +---- +`hello \("world")\ud83c\udf0d` +---- +results in the string `hello \("world")\ud83c\udf0d` or as JSON `"hello \\(\"world\")\\ud83c\\udf0d"`. +In contrast ++ +---- +"hello \("world")\ud83c\udf0d" +---- +results in the string `hello world🌍` or as JSON `"hello world🌍"`. ++ +* Supports more number bases in integer literals. +** `0xabcd` (Hexadecimal). +** `0o125715` (Octal). +** `0b1010101111001101` (Binary). +** Grouping using underscore `0xab_cd`. +* Binary and decode value types, see below. +* Try include using an ending question mark `include "file?";` that doesn't fail if file is missing or has errors. +* Some values can act as an object with keys even when they are arrays, numbers etc. +* There can be keys hidden from `keys` and `[]`. +* Some values are readonly and can't be updated or will convert to JSON on update. +* Mixing `--args` and `--jsonargs` does not behave the same. + +=== Additional functions + +`*band*`, `*bor*`, `*bxor*`, `*bsl*`, `*bsr*`, `*bnot*`:: + Bitwise operations as functions. Works the same as jq's math functions. Functions that take one argument use + input, `1 | bnot`, and functions with more arguments ignore the input and use formal arguments `bsl(1; 3)`. + +`*chunk($size)*`:: + Split array or string into `$size` length chunks. Last chunk might be shorter. + +`*count*`, `*count_by(f)*`:: + Like `group` but outputs array of `[value, count]` pairs. + +`*delta*`, `*delta_by(f)*`:: + Array with difference between consecutive. `delta` is the same as `delta_by(.b - .a)`. + +`*diff($a; $b)*`:: + Produce a diff between `$a` and `$b`. Differences are represented as an object `{a: , b: }`. + +`*expr_to_path*`:: + Converts from a string `".key[1]"` to a path value `["key", 1]`. + +`*grep_by(f)*`:: + Recursively select using a filter and ignore any errors. + Ex: `grep_by(. > 180 and . < 200)`, `first(grep_by(format == "id3v2"))`. + This is the same as doing `.. | select(f)?`. + +`*group*`:: + Group values, same as `group_by(.)`. + +`*path_to_expr*`:: + Converts a path value `["key", 1]` to a string `".key[1]"`. + +`*paste*`:: + Read string from stdin until ^D. Useful for pasting text. Ex: `paste | from_pem | asn1_ber | repl` read from stdin then decode and start a new sub-REPL with result. + +`*streaks*`, `*streaks_by(f)*`:: + Like `group` but groups streaks based on condition. + +`*repl*`, `*repl($opts)*`:: + Nested REPL. Must be last in a pipeline. `repl` can "slurp" outputs, ex: `1, 2, 3 | repl`, and supports options, ex: `[1,2,3] | repl({compact: true})`. + +`*slurp("")*`:: + Slurp outputs and saves them to `$name`. Must be last in the pipeline. Will be available as a global array `$name`. Ex `1,2,3 | slurp("a")`, `$a[]` same as `spew("a")`. + +`*spew*`, `*spew("")*`:: + Outputs all or a specific slurp. Ex: `spew("a")`. + +`*println*`, `*print*`:: +Print string or compact JSON to stdout with and without new line. + +`*printerrln*`, `*printerr*`:: +Print string or compact JSON to stderr with and without new line. + +=== Decode value + +A decode value is the type returned from decoding a format and used to represent values produced by a decoder. +It can be seen as representing any standard jq type but with some additional properties attached. + +Each decode value has these properties: + +* Bit range in the input. Can be used as a binary using `tobytes`, `tobytesrange`, `tobits` and `tobitsrange`. +* If scalar type, an actual value: +** This is the decoded representation of the bits, a number, string, bool etc. +** Can be accessed using `toactual`. +* If scalar type, an optional symbolic value: +** Is usually a mapping of the actual to symbolic value, ex: map number to a string value. +** Can be accessed using `tosym`. +* An optional description: +** Can be accessed using `todescription`. +* `parent` is the parent decode value +* `parents` is all the parent decode values +* `topath` is the jq path for the decode value +* `torepr` converts decode value to its representation if possible + +The value of a decode value is the symbolic value if available and otherwise the actual value. To explicitly access the value use `tovalue`. In most expressions this is not needed as it will be done automatically. + +==== Decode value functions + +`*root*`:: + Root decode value for decode value. + +`*buffer_root*`:: + Root decode value of sub buffer for decode value. + +`*format_root*`:: + Root decode value of nested format for decode value. + +`*parent*`:: + Parent decode value for decode value. + +`*parents*`:: + Outputs all parent decode values from decode value. + +`*topath*`:: + Path for decode value. Use `path_to_expr` to get a string representation. + +`*tovalue`, `tovalue($opts)*`:: + Symbolic, if available, or actual value for decode value. + +`*toactual`, `toactual($opts)*`:: + Actual value for decode value. + +`*tosym`, `tosym($opts)*`:: + Symbolic value for decode value. + +`*todescription*`:: + Description for decode value. + +`*torepr*`:: + Converts decode value into what it represents. For example converts msgpack decode value into a value representing its JSON representation. + +`tobytes`, `tobytesrange`, `tobits` and `tobitsrange` on a decode value will return the raw source bits as a binary. + +=== Binary + +Binary type is used to store raw bits or bytes. Raw bits will act as zero bits padded strings in standard jq expressions. + +Use `tobits` and `tobytes` to create them from decode value, string, number or binary array. `tobytes` will if needed zero pad most significant bits to be byte aligned. + +There is also `tobitsrange` and `tobytesrange` which do the same thing but will preserve source range when displayed. + +- `"string" | tobytes` produces a binary with UTF8 bytes. +- `1234 | tobits` produces a binary with the unsigned big-endian integer 1234 with enough bits to represent the number. Use `tobytes` to get the same but with enough bytes to represent the number. This is different to how numbers work inside binary arrays where they are limited to 0-255. +- `["abc", 123, ...] | tobytes` produces a binary from a binary array. See <> below. +- `.[index]` access bit or byte at index `index`. Index is in units. + - `[0x12, 0x34, 0x56] | tobytes[1]` is `0x34` + - `[0x12, 0x34, 0x56] | tobits[3]` is `1` +- `.[start:]`, `.[start:end]` or `.[:end]` is normal jq slice syntax and will slice the binary from `start` to `end`. `start` and `end` are in units. + - `[0x12, 0x34, 0x56] | tobytes[1:2]` will be a binary with the byte `0x34` + - `[0x12, 0x34, 0x56] | tobits[4:12]` will be a binary with the byte `0x23` + - `[0x12, 0x34, 0x56] | tobits[4:20]` will be a binary with the bytes `0x23`, `0x45` + - `[0x12, 0x34, 0x56] | tobits[4:20] | tobytes[1:]` will be a binary with the byte `0x45` + - Both `.[index]` and `.[start:end]` support negative indices to index from end. +- `explode` outputs an array with all bytes or bits as integers. + +==== Binary functions + +`*grep($v)*`, `*grep($v; $flags)*`, `*vgrep($v)*`, `*vgrep($v; $flags)*`, `*bgrep($v)*`, `*bgrep($v; $flags)*`:: + Recursively match `$v`. + `$v` is a scalar to match, where a string is treated as a regexp. A binary will match exact bytes. + `$flags` arguments are regexp flags with additional flag "b" that will treat each byte in the input binary + as a code point. This makes it possible to match exact bytes. + +`*fgrep($v)*`, `*fgrep($v; $flags)*`:: + Recursively match field name in a decode value. + +`*tobits*`:: + Transform input to binary with bit as unit and don't preserve source range. + +`*tobitsrange*`:: + Transform input to binary with bit as unit and preserve source range. + +`*tobytes*`:: + Transform input to binary with byte as unit and don't preserve source range. + +`*tobytesrange*`:: + Transform input to binary with byte as unit and preserve source range. + +`*open*`:: + Open file for reading. + +=== Binary array [[binary_array]] + +Binary array is a value "shape" and not a new type. It's an array of numbers, strings, binaries or other +binary arrays. They can be used as input to `tobits`, `tobytes` or other function that accept a binary as input. + +- Number is a byte with value 0-255 +- String as UTF8 bytes +- Binary as is +- Binary array used recursively + +Binary arrays are similar to and inspired by https://www.erlang.org/doc/man/erlang.html#type-iolist[Erlang iolist]. + +Some examples: + +- `[0, 123, 255] | tobytes` will be binary with 3 bytes 0, 123 and 255. +- `[0, [123, 255]] | tobytes` same as above. +- `[0, 1, 1, 0, 0, 1, 1, 0 | tobits] | tobytes` will be binary with 1 byte, 0x66. +- `[(.a | tobytes[-10:]), 255, (.b | tobits[:10])] | tobytes` the concatenation of the last 10 bytes of `.a`, byte of value 255 and the first 10 bits of `.b`. + +=== Differences to jq + +See https://github.com/itchyny/gojq#difference-to-jq[gojq's differences to jq]. + +=== Naming inconsistencies + +jq's naming convention is a bit inconsistent. Some standard library functions are named `tojson` while others `from_entries`. fq follows this tradition but tries to use `snake_case` unless there is a good reason. + +Here are all the non-snake_case functions added by fq. Most of them deal with decode and binary values which are new "primitive" types: + +- `toactual` +- `tobits` +- `tobitsrange` +- `tobytes` +- `tobytesrange` +- `todescription` +- `topath` +- `torepr` +- `tosym` +- `tovalue` + +== Display output + +`display` or `d` is the main function for displaying values and is also the function that will be used if no other output function is explicitly used. If its input is a decode value it will output a dump and tree structure or otherwise it will output as JSON. + +Below demonstrates some usages: + +The first and second examples do the same thing, inputting `"hello"` to `display`. + +[source,shell] +---- +$ fq -n '"hello"' +"hello" + +$ fq -n '"hello" | d' +"hello" +---- + + +In the next few examples we select out the first "edit list" box in an mp4 file and display it in various ways. + +By default, display will only show the root level: + +[source,shell] +---- +$ fq 'first(grep_by(.type == "elst"))' file.mp4 + |00 01 02 03 04 05 06 07 08 09|0123456789|.boxes[3].boxes[1].boxes[1].boxes[0]{}: box +0xd5c| 00| .| size: 28 +0xd66|00 00 1c |... | +0xd66| 65 6c 73 74 | elst | type: "elst" (An edit list) +0xd66| 00 | . | version: 0 +0xd66| 00 00| ..| flags: 0 +0xd70|00 |. | +0xd70| 00 00 00 01 | .... | entry_count: 1 +0xd70| 00 00 00 28 00| ...(.| entries[0:1]: +0xd7a|00 00 00 00 01 00 00 |....... | +---- + + +First row shows a ruler with byte offset into the line and jq path for the value. + +The columns are: + +- Start address for the line. For example we see that `size` starts at `0xd5c` (row) + `0x09` (column) = `0xd65`. +- Hex representation of input bits for value. Will show the whole byte even if the value only partially uses bits from it. +- ASCII representation of input bits for value. Will show the whole byte even if the value only partially uses bits from it. +- Tree structure of decoded value, symbolic value and description. + +Notation: + +- `{}` value is an object that might have nested values. +- `[start:end]` value is an array with index starting at `start` and ending at `end` (exclusive). + +With `display` or `d` it will recursively show the whole tree: + +[source,shell] +---- +$ fq 'first(grep_by(.type == "elst")) | d' file.mp4 + |00 01 02 03 04 05 06 07 08 09|0123456789|.boxes[3].boxes[1].boxes[1].boxes[0]{}: box +0xd5c| 00| .| size: 28 +0xd66|00 00 1c |... | +0xd66| 65 6c 73 74 | elst | type: "elst" (An edit list) +0xd66| 00 | . | version: 0 +0xd66| 00 00| ..| flags: 0 +0xd70|00 |. | +0xd70| 00 00 00 01 | .... | entry_count: 1 + | | | entries[0:1]: + | | | [0]{}: entry +0xd70| 00 00 00 28 | ...( | segment_duration: 40 +0xd70| 00| .| media_time: 0 +0xd7a|00 00 00 |... | +0xd7a| 00 01 00 00 | .... | media_rate: 1 +---- + + +Same but verbose `dv`: + +[source,shell] +---- +$ fq 'first(grep_by(.type == "elst")) | dv' file.mp4 + |00 01 02 03 04 05 06 07 08 09|0123456789|.boxes[3].boxes[1].boxes[1].boxes[0]{}: box 0xd65-0xd81 (28) +0xd5c| 00| .| size: 28 0xd65-0xd69 (4) +0xd66|00 00 1c |... | +0xd66| 65 6c 73 74 | elst | type: "elst" (An edit list) 0xd69-0xd6d (4) +0xd66| 00 | . | version: 0 0xd6d-0xd6e (1) +0xd66| 00 00| ..| flags: 0 0xd6e-0xd71 (3) +0xd70|00 |. | +0xd70| 00 00 00 01 | .... | entry_count: 1 0xd71-0xd75 (4) + | | | entries[0:1]: 0xd75-0xd81 (12) + | | | [0]{}: entry 0xd75-0xd81 (12) +0xd70| 00 00 00 28 | ...( | segment_duration: 40 0xd75-0xd79 (4) +0xd70| 00| .| media_time: 0 0xd79-0xd7d (4) +0xd7a|00 00 00 |... | +0xd7a| 00 01 00 00 | .... | media_rate: 1 0xd7d-0xd81 (4) +---- + + +In verbose mode bit ranges and array element names are shown. + +Bit ranges use `[.]-[.]` as notation where `.` is left out if byte aligned. For example `type` starts at byte `0xd69` bit `0` (`.0` is left out) and ends at `0xd6d` bit `0` (exclusive) and has a size of `4` bytes. + +This verbosely displays the header of the second frame in an mp3 file which has a bunch of non-byte-aligned fields: + +[source,shell] +---- +$ fq '.frames[1].header | dv' file.mp3 + |00 01 02 03 04 05 06 07 08 09|0123456789|.frames[1].header{}: 0xb79-0xb7d (4) +0xb72| ff fb | .. | sync: 0b11111111111 (valid) 0xb79-0xb7a.3 (1.3) +0xb72| fb | . | mpeg_version: "1" (3) (MPEG Version 1) 0xb7a.3-0xb7a.5 (0.2) +0xb72| fb | . | layer: 3 (1) (MPEG Layer 3) 0xb7a.5-0xb7a.7 (0.2) + | | | sample_count: 1152 +0xb72| fb | . | protection_absent: true (No CRC) 0xb7a.7-0xb7b (0.1) +0xb72| 50| P| bitrate: 64000 (5) 0xb7b-0xb7b.4 (0.4) +0xb72| 50| P| sample_rate: 44100 (0) 0xb7b.4-0xb7b.6 (0.2) +0xb72| 50| P| padding: "not_padded" (0b0) 0xb7b.6-0xb7b.7 (0.1) +0xb72| 50| P| private: 0 0xb7b.7-0xb7c (0.1) +0xb7c|c4 |. | channels: "mono" (0b11) 0xb7c-0xb7c.2 (0.2) +0xb7c|c4 |. | channel_mode: "none" (0b0) 0xb7c.2-0xb7c.4 (0.2) +0xb7c|c4 |. | copyright: 0 0xb7c.4-0xb7c.5 (0.1) +0xb7c|c4 |. | original: 1 0xb7c.5-0xb7c.6 (0.1) +0xb7c|c4 |. | emphasis: "none" (0b0) 0xb7c.6-0xb7d (0.2) +---- + + +Here the `sync` pattern starts at `0xb79` (bit `0`) and ends at `0xb7a.3` (exclusive) and has a size of `1` byte and `3` bits, `11` bits in total (`8+3`). + +There are also some other `display` aliases: + +- `da` is `display({array_truncate: 0, string_truncate: 0})` don't truncate array and strings. +- `dd` is `display({array_truncate: 0, string_truncate: 0, display_bytes: 0})` don't truncate array and strings, show all raw bytes. +- `dv` is `display({array_truncate: 0, string_truncate: 0, verbose: true})` don't truncate array and strings and display verbosely. +- `ddv` is `display({array_truncate: 0, string_truncate: 0, display_bytes: 0, verbose: true})` don't truncate array and strings, show all raw bytes and display verbosely. + +== Formats + +By default fq will try to automatically determine input format. In some cases this might fail or is not +possible, then a format can be specified using `-d NAME`. It's possible sometimes to force decode and get +a partial or broken result using `-o force=true`. + +[source,console] +---- +# decode as msgpack +$ fq -d msgpack d file +# force decode as msgpack +$ fq -d msgpack -o force=true d file +# see msgpack format help +$ fq -h msgpack +# list supported formats +$ fq -h formats +---- + +=== Format options + +Some formats has own options that can be set using `-o`. For +example the `mp4` format has a `decode_samples` option that controls +if individual samples should be decoded. To disable it one can do +`fq -o decode_samples=false . file.mp4`. See format list for options. + +=== Format functions + +In addition to using `-d` all format decoders are also available as normal jq functions. +Each format provides multiple functions: + +``:: + Decode and return a decode value even on error. Ex: `... | mp4` + +`($options)`:: + Same as above with format options. Ex: `... | mp4({decode_samples: false})` + +`from_`:: + Decode or throw on error. Ex: `... | from_mp4` + +`from_($options)`:: + Same as above with format options Ex: `... | from_mp4({decode_samples: false})` + +Example usage: + +[source,console] +---- +# decode jpeg found inside some other format +$ fq '.some[].query | jpeg' file + +# decode jpeg at byte range 100-200 +$ fq -d bytes '.[100:200] | jpeg' file +---- + +=== Supported formats + +`*aac_frame*`:: + Advanced Audio Coding frame ++ +-- + +.Options +`*-o object_type=1*`::: + Audio object type + +-- ++ + +`*adts*`:: + Audio Data Transport Stream + +`*adts_frame*`:: + Audio Data Transport Stream frame + +`*aiff*`:: + Audio Interchange File Format + +`*amf0*`:: + Action Message Format 0 + +`*apev2*`:: + APEv2 metadata tag + +`*apple_bookmark*`:: + Apple BookmarkData ++ +-- +Apple's `bookmarkData` format is used to encode information that can be resolved into a `URL` object for a file even if the user moves or renames it. Can also contain security scoping information for App Sandbox support. +These `bookmarkData` blobs are often found encoded in data fields of Binary Property Lists. Notable examples include: + +- `com.apple.finder.plist` - contains an `FXRecentFolders` value, which is an array of ten objects, each of which consists of a `name` and `file-bookmark` field, which is a `bookmarkData` object for each recently accessed folder location. +- `com.apple.LSSharedFileList.RecentApplications.sfl2` - `sfl2` files are actually `plist` files of the `NSKeyedArchiver` format. They can be parsed the same as `plist` files, but they have a more complicated tree-like structure than would typically be found, which can make locating and retrieving specific values difficult, even once it has been converted to a JSON representation. For more information about these types of files, see Sarah Edwards' excellent research on the subject (link in references). + +//- +`fq`'s `grep_by` function can be used to recursively descend through the decoded tree, probing for and selecting any `bookmark` blobs, then converting them to readable JSON with `torepr`: +[source,console] +---- +fq 'grep_by(.type=="data" and .value[0:4] == "book") | .value | apple_bookmark | +torepr' +---- + +Authors::: + + +- David McDonald https://github.com/dgmcdona[@dgmcdona] https://twitter.com/river_rat_504[@river_rat_504] + +//- + +References::: + + +- https://developer.apple.com/documentation/foundation/url/2143023-bookmarkdata +- https://mac-alias.readthedocs.io/en/latest/bookmark_fmt.html +- https://www.mac4n6.com/blog/2016/1/1/manual-analysis-of-nskeyedarchiver-formatted-plist-files-a-review-of-the-new-os-x-1011-recent-items +- https://michaellynn.github.io/2015/10/24/apples-bookmarkdata-exposed/ + +//- +-- ++ + +`*ar*`:: + Unix archive + +`*asn1_ber*`:: + ASN1 BER (basic encoding rules, also CER and DER) ++ +-- +Supports decoding BER, CER and DER (X.690). + +- Currently no extra validation is done for CER and DER. +- Does not support specifying a schema. +- Supports `torepr` but without schema all sequences and sets will be arrays. + +//- + +Can be used to decode certificates etc::: + +[source,console] +---- +$ fq -d bytes 'from_pem | asn1_ber | d' cert.pem +---- + +Can decode nested values::: + +[source,console] +---- +$ fq -d asn1_ber '.constructed[1].value | asn1_ber' file.ber +---- + +Manual schema::: + +[source,console] +---- +$ fq -d asn1_ber 'torepr as $r | ["version", "modulus", "private_exponent", "prime1", "prime2", "exponent1", "exponent2", "coefficient"] | with_entries({key: .value, value: $r[.key]})' pkcs1.der +---- + +References::: + + +- https://www.itu.int/ITU-T/studygroups/com10/languages/X.690_1297.pdf +- https://en.wikipedia.org/wiki/X.690 +- https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ +- https://lapo.it/asn1js/ + +//- +-- ++ + +`*av1_ccr*`:: + AV1 Codec Configuration Record + +`*av1_frame*`:: + AV1 frame + +`*av1_obu*`:: + AV1 Open Bitstream Unit + +`*avc_annexb*`:: + H.264/AVC Annex B + +`*avc_au*`:: + H.264/AVC Access Unit ++ +-- + +.Options +`*-o bottom_field_pic_order_in_frame_present_flag=false*`::: + No description + +`*-o cpb_cnt=0*`::: + No description + +`*-o cpb_removal_delay_length=0*`::: + No description + +`*-o delta_pic_order_always_zero_flag=false*`::: + No description + +`*-o dpb_output_delay_length=0*`::: + No description + +`*-o frame_mbs_only_flag=true*`::: + No description + +`*-o initial_cpb_removal_delay_length=0*`::: + No description + +`*-o length_size=0*`::: + Length value size + +`*-o log2max_frame_num=4*`::: + No description + +`*-o log2max_pic_order_cnt_lsb=4*`::: + No description + +`*-o nal_hrd_parameters_present=false*`::: + No description + +`*-o pic_order_cnt_type=0*`::: + No description + +`*-o redundant_pic_cnt_present_flag=false*`::: + No description + +`*-o separate_colour_plane_flag=false*`::: + No description + +`*-o time_offset_length=0*`::: + No description + +`*-o vcl_hrd_parameters_present=false*`::: + No description + +-- ++ + +`*avc_dcr*`:: + H.264/AVC Decoder Configuration Record + +`*avc_nalu*`:: + H.264/AVC Network Access Layer Unit ++ +-- + +.Options +`*-o bottom_field_pic_order_in_frame_present_flag=false*`::: + No description + +`*-o cpb_cnt=0*`::: + No description + +`*-o cpb_removal_delay_length=0*`::: + No description + +`*-o delta_pic_order_always_zero_flag=false*`::: + No description + +`*-o dpb_output_delay_length=0*`::: + No description + +`*-o frame_mbs_only_flag=true*`::: + No description + +`*-o initial_cpb_removal_delay_length=0*`::: + No description + +`*-o log2max_frame_num=4*`::: + No description + +`*-o log2max_pic_order_cnt_lsb=4*`::: + No description + +`*-o nal_hrd_parameters_present=false*`::: + No description + +`*-o pic_order_cnt_type=0*`::: + No description + +`*-o redundant_pic_cnt_present_flag=false*`::: + No description + +`*-o separate_colour_plane_flag=false*`::: + No description + +`*-o time_offset_length=0*`::: + No description + +`*-o vcl_hrd_parameters_present=false*`::: + No description + +-- ++ + +`*avc_pps*`:: + H.264/AVC Picture Parameter Set + +`*avc_sei*`:: + H.264/AVC Supplemental Enhancement Information ++ +-- + +.Options +`*-o cpb_cnt=0*`::: + No description + +`*-o cpb_removal_delay_length=0*`::: + No description + +`*-o delta_pic_order_always_zero_flag=false*`::: + No description + +`*-o dpb_output_delay_length=0*`::: + No description + +`*-o frame_mbs_only_flag=true*`::: + No description + +`*-o initial_cpb_removal_delay_length=0*`::: + No description + +`*-o log2max_frame_num=4*`::: + No description + +`*-o log2max_pic_order_cnt_lsb=4*`::: + No description + +`*-o nal_hrd_parameters_present=false*`::: + No description + +`*-o pic_order_cnt_type=0*`::: + No description + +`*-o separate_colour_plane_flag=false*`::: + No description + +`*-o time_offset_length=0*`::: + No description + +`*-o vcl_hrd_parameters_present=false*`::: + No description + +-- ++ + +`*avc_sps*`:: + H.264/AVC Sequence Parameter Set + +`*avi*`:: + Audio Video Interleaved ++ +-- + +Samples::: + +AVI has many redundant ways to index samples so currently `.streams[].samples` will only include samples the most "modern" way used in the file. That is in order of stream super index, movi ix index then idx1 index. + +Extract samples for stream 1::: + +[source,console] +---- +$ fq '.streams[1].samples[] | tobytes' file.avi > stream01.mp3 +---- + +Show stream summary::: + +[source,console] +---- +$ fq -o decode_samples=false '[.chunks[0] | grep_by(.id=="LIST" and .type=="strl") | grep_by(.id=="strh") as {$type} | grep_by(.id=="strf") as {$format_tag, $compression} | {$type,$format_tag,$compression}]' *.avi +---- + +Speed up decoding by disabling sample and extended chunks decoding::: + +If you're not interested in sample details or extended chunks you can speed up decoding by using: +[source,console] +---- +$ fq -o decode_samples=false -o decode_extended_chunks=false d file.avi +---- + +References::: + + +- https://learn.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference[AVI RIFF File Reference] +- http://www.jmcgowan.com/odmlff2.pdf[OpenDML AVI File Format Extensions] + +//- + +.Options +`*-o decode_extended_chunks=true*`::: + Decode extended chunks + +`*-o decode_samples=true*`::: + Decode samples + +-- ++ + +`*avro_ocf*`:: + Avro object container file ++ +-- +Supports reading Avro Object Container Format (OCF) files based on the 1.11.0 specification. +Capable of handling null, deflate, and snappy codecs for data compression. +Limitations: + +- Schema does not support self-referential types, only built-in types. +- Decimal logical types are not supported for decoding, will just be treated as their primitive type + +//- + +References::: + + +- https://avro.apache.org/docs/current/spec.html#Object+Container+Files + +//- + +Authors::: + + +- Xentripetal xentripetal@fastmail.com https://github.com/xentripetal[@xentripetal] + +//- +-- ++ + +`*bencode*`:: + BitTorrent bencoding ++ +-- + +Convert represented value to JSON::: + +[source,console] +---- +$ fq -d bencode torepr file.torrent +---- + +References::: + + +- https://wiki.theory.org/BitTorrentSpecification#Bencoding + +//- +-- ++ + +`*bitcoin_blkdat*`:: + Bitcoin blk.dat + +`*bitcoin_block*`:: + Bitcoin block ++ +-- + +.Options +`*-o has_header=false*`::: + Has blkdat header + +-- ++ + +`*bitcoin_script*`:: + Bitcoin script + +`*bitcoin_transaction*`:: + Bitcoin transaction + +`*bits*`:: + Raw bits ++ +-- +Decode to a slice and indexable binary of bits. + +Slice and decode bit range::: + +[source,console] +---- +$ echo 'some {"a":1} json' | fq -d bits '.[40:-48] | fromjson' +{ + "a": 1 +} +---- + +Index bits::: + +[source,console] +---- +$ echo 'hello' | fq -d bits '.[4]' +1 +$ echo 'hello' | fq -c -d bits '[.[range(8)]]' +[0,1,1,0,1,0,0,0] +---- +-- ++ + +`*bplist*`:: + Apple Binary Property List ++ +-- + +Show full decoding::: + +[source,console] +---- +$ fq d Info.plist +---- + +Timestamps::: + +Timestamps in Apple Binary Property Lists are encoded as Cocoa Core Data timestamps, where the raw value is the floating point number of seconds since January 1, 2001. By default, `fq` will render the raw floating point value. In order to get the raw value or the string description, use the `tovalue` or `todescription` functions: +[source,console] +---- +$ fq 'torepr.SomeTimeStamp | tovalue' Info.plist +685135328 + +$ fq 'torepr.SomeTimeStamp | todescription' Info.plist +"2022-09-17T19:22:08Z" +---- + +Get JSON representation::: + +`bplist` files can be converted to a JSON representation using the `torepr` filter: +[source,console] +---- +$ fq torepr com.apple.UIAutomation.plist +{ + "UIAutomationEnabled": true +} +---- + +Decoding NSKeyedArchiver serialized objects::: + +A common way that Swift and Objective-C libraries on macOS serialize objects is through the NSKeyedArchiver API, which flattens objects into a list of elements and class descriptions that are reconstructed into an object graph using CFUID elements in the property list. `fq` includes a function, `from_ns_keyed_archiver`, which will rebuild this object graph into a friendly representation. +If no parameters are supplied, it will assume that there is a CFUID located at `."$top".root` that specifies the root from which decoding should occur. If this is not present, an error will be produced, asking the user to specify a root object in the `.$objects` list from which to decode. +The following examples show how this might be used (in this case, within the `fq` REPL): +[source,console] +---- +# Assume $top.root is present +bplist> from_ns_keyed_archiver + +# Specify optional root +bplist> from_ns_keyed_archiver(1) +---- + +Authors::: + + +- David McDonald https://github.com/dgmcdona[@dgmcdona] + +//- + +References::: + + +- http://fileformats.archiveteam.org/wiki/Property_List/Binary +- https://medium.com/@karaiskc/understanding-apples-binary-property-list-format-281e6da00dbd +- https://opensource.apple.com/source/CF/CF-550/CFBinaryPList.c + +//- +-- ++ + +`*bsd_loopback_frame*`:: + BSD loopback frame + +`*bson*`:: + Binary JSON ++ +-- + +Limitations::: + + +- The decimal128 type is not supported for decoding, will just be treated as binary + +//- + +Convert represented value to JSON::: + +[source,console] +---- +$ fq -d bson torepr file.bson +---- + +Filter represented value::: + +[source,console] +---- +$ fq -d bson 'torepr | select(.name=="bob")' file.bson +---- + +Authors::: + + +- Mattias Wadman mattias.wadman@gmail.com, original author +- Matt Dale https://github.com/matthewdale[@matthewdale], additional types and bug fixes + +//- + +References::: + + +- https://bsonspec.org/spec.html + +//- +-- ++ + +`*bytes*`:: + Raw bytes ++ +-- +Decode to a slice and indexable binary of bytes. + +Slice out byte ranges::: + +[source,console] +---- +$ echo -n 'hello' | fq -d bytes '.[-3:]' > last_3_bytes +$ echo -n 'hello' | fq -d bytes '[.[-2:], .[0:2]] | tobytes' > first_last_2_bytes_swapped +---- + +Slice and decode byte range::: + +[source,console] +---- +$ echo 'some {"a":1} json' | fq -d bytes '.[5:-6] | fromjson' +{ + "a": 1 +} +---- + +Index bytes::: + +[source,console] +---- +$ echo 'hello' | fq -d bytes '.[1]' +101 +---- +-- ++ + +`*bzip2*`:: + bzip2 compression + +`*caff*`:: + Live2D Cubism archive ++ +-- + +Authors::: + + +- https://github.com/ronsor[@ronsor] + +//- + +.Options +`*-o uncompress=true*`::: + Uncompress and probe files + +-- ++ + +`*cbor*`:: + Concise Binary Object Representation ++ +-- + +Convert represented value to JSON::: + +[source,console] +---- +$ fq -d cbor torepr file.cbor +---- + +References::: + + +- https://en.wikipedia.org/wiki/CBOR +- https://www.rfc-editor.org/rfc/rfc8949.html + +//- +-- ++ + +`*csv*`:: + Comma separated values ++ +-- + +TSV to CSV::: + +[source,console] +---- +$ fq -d csv -o comma="\t" to_csv file.tsv +---- + +Convert rows to objects based on header row::: + +[source,console] +---- +$ fq -d csv '.[0] as $t | .[1:] | map(with_entries(.key = $t[.key]))' file.csv +---- + +.Options +`*-o comma=","*`::: + Separator character + +`*-o comment="#"*`::: + Comment line character + +-- ++ + +`*dns*`:: + DNS packet + +`*dns_tcp*`:: + DNS packet (TCP) + +`*elf*`:: + Executable and Linkable Format + +`*ether8023_frame*`:: + Ethernet 802.3 frame + +`*exif*`:: + Exchangeable Image File Format + +`*fairplay_spc*`:: + FairPlay Server Playback Context + +`*fit*`:: + Garmin Flexible and Interoperable Data Transfer ++ +-- + +Limitations::: + + +- Fields with subcomponents, such as "compressed_speed_distance" field on globalMessageNumber 20 is not represented correctly. The field is read as 3 separate bytes where the first 12 bits are speed and the last 12 bits are distance. +- There are still lots of UNKNOWN fields due to gaps in Garmin's SDK Profile documentation. (Currently FIT SDK 21.126) +- Compressed timestamp messages are not accumulated against last known full timestamp. + +//- + +Convert stream of data messages to JSON array::: + +[source,console] +---- +$ fq '[.data_records[] | select(.record_header.message_type == "data").data_message]' file.fit +---- + +Authors::: + + +- Mikael Lofjärd mikael.lofjard@gmail.com, original author + +//- + +References::: + + +- https://developer.garmin.com/fit/protocol/ +- https://developer.garmin.com/fit/cookbook/decoding-activity-files/ + +//- +-- ++ + +`*flac*`:: + Free Lossless Audio Codec file + +`*flac_frame*`:: + FLAC frame ++ +-- + +.Options +`*-o bits_per_sample=16*`::: + Bits per sample + +`*-o sample_details=false*`::: + Decode more sample details like residuals etc + +-- ++ + +`*flac_metadatablock*`:: + FLAC metadatablock + +`*flac_metadatablocks*`:: + FLAC metadatablocks + +`*flac_picture*`:: + FLAC metadatablock picture + +`*flac_streaminfo*`:: + FLAC streaminfo + +`*gif*`:: + Graphics Interchange Format + +`*gzip*`:: + gzip compression + +`*heif*`:: + High Efficiency Image Format ++ +-- + +.Options +`*-o allow_truncated=false*`::: + Allow box to be truncated + +-- ++ + +`*hevc_annexb*`:: + H.265/HEVC Annex B + +`*hevc_au*`:: + H.265/HEVC Access Unit ++ +-- + +.Options +`*-o length_size=4*`::: + Length value size + +-- ++ + +`*hevc_dcr*`:: + H.265/HEVC Decoder Configuration Record + +`*hevc_nalu*`:: + H.265/HEVC Network Access Layer Unit + +`*hevc_pps*`:: + H.265/HEVC Picture Parameter Set + +`*hevc_sps*`:: + H.265/HEVC Sequence Parameter Set + +`*hevc_vps*`:: + H.265/HEVC Video Parameter Set + +`*html*`:: + HyperText Markup Language ++ +-- +HTML is decoded in HTML5 mode and will always include ``, `` and `` elements. +See xml format for more examples and how to preserve element order and how to encode to xml. +There is no `to_html` function, see `to_xml` instead. + +Element as object::: + +[source,console] +---- +# decode as object is the default +$ echo 'text' | fq -d html +{ + "html": { + "body": { + "a": { + "#text": "text", + "@href": "url" + } + }, + "head": "" + } +} +---- + +Element as array::: + +[source,console] +---- +$ echo 'text' | fq -d html -o array=true +[ + "html", + null, + [ + [ + "head", + null, + [] + ], + [ + "body", + null, + [ + [ + "a", + { + "#text": "text", + "href": "url" + }, + [] + ] + ] + ] + ] +] + +# decode html files to a {file: "title", ...} object +$ fq -n -d html '[inputs | {key: input_filename, value: .html.head.title?}] | from_entries' *.html + +# href:s in file +$ fq -r -o array=true -d html '.. | select(.[0] == "a" and .[1].href)?.[1].href' file.html +---- + +.Options +`*-o array=false*`::: + Decode as nested arrays + +`*-o attribute_prefix="@"*`::: + Prefix for attribute keys + +`*-o seq=false*`::: + Use seq attribute to preserve element order + +-- ++ + +`*icc_profile*`:: + International Color Consortium profile + +`*icmp*`:: + Internet Control Message Protocol + +`*icmpv6*`:: + Internet Control Message Protocol v6 + +`*id3v1*`:: + ID3v1 metadata + +`*id3v11*`:: + ID3v1.1 metadata + +`*id3v2*`:: + ID3v2 metadata + +`*ipv4_packet*`:: + Internet protocol v4 packet + +`*ipv6_packet*`:: + Internet protocol v6 packet + +`*jp2c*`:: + JPEG 2000 codestream + +`*jpeg*`:: + Joint Photographic Experts Group file + +`*json*`:: + JavaScript Object Notation + +`*jsonl*`:: + JavaScript Object Notation Lines + +`*leveldb_descriptor*`:: + LevelDB Descriptor ++ +-- + +Limitations::: + + +- fragmented non-"full" records are not merged and decoded further. + +//- + +Authors::: + + +- https://github.com/mikez[@mikez], original author + +//- + +References::: + + +- https://github.com/google/leveldb/blob/main/doc/impl.md#manifest +- https://github.com/google/leveldb/blob/main/doc/log_format.md +- https://github.com/google/leveldb/blob/main/db/version_edit.cc + +//- +-- ++ + +`*leveldb_log*`:: + LevelDB Log ++ +-- + +Limitations::: + + +- fragmented non-"full" records are not merged and decoded further. + +//- + +Authors::: + + +- https://github.com/mikez[@mikez], original author + +//- + +References::: + + +- https://github.com/google/leveldb/blob/main/doc/impl.md#log-files +- https://github.com/google/leveldb/blob/main/doc/log_format.md +- https://github.com/google/leveldb/blob/main/db/write_batch.cc + +//- +-- ++ + +`*leveldb_table*`:: + LevelDB Table ++ +-- + +Limitations::: + + +- no Meta Blocks (like "filter") are decoded yet. +- Zstandard uncompression is not implemented yet. + +//- + +Authors::: + + +- https://github.com/mikez[@mikez], original author + +//- + +References::: + + +- https://github.com/google/leveldb/blob/main/doc/table_format.md +- https://github.com/google/leveldb/blob/main/doc/impl.md +- https://github.com/google/leveldb/blob/main/doc/index.md + +//- +-- ++ + +`*luajit*`:: + LuaJIT 2.0 bytecode ++ +-- + +Authors::: + + +- https://github.com/dlatchx[@dlatchx] + +//- + +References::: + + +- https://github.com/LuaJIT/LuaJIT/blob/v2.1/src/lj_bcdump.h +- http://scm.zoomquiet.top/data/20131216145900/index.html + +//- +-- ++ + +`*macho*`:: + Mach-O macOS executable ++ +-- +Supports decoding vanilla and FAT Mach-O binaries. + +Select 64bit load segments::: + +[source,console] +---- +$ fq '.load_commands[] | select(.cmd=="segment_64")' file +---- + +References::: + + +- https://github.com/aidansteele/osx-abi-macho-file-format-reference + +//- + +Authors::: + + +- Sıddık AÇIL acils@itu.edu.tr https://github.com/Akaame[@Akaame] + +//- +-- ++ + +`*macho_fat*`:: + Fat Mach-O macOS executable (multi-architecture) + +`*markdown*`:: + Markdown ++ +-- + +Array with all level 1 and 2 headers::: + +[source,console] +---- +$ fq -d markdown '[.. | select(.type=="heading" and .level<=2)?.children[0]]' file.md +---- +-- ++ + +`*matroska*`:: + Matroska file ++ +-- + +Lookup element using path::: + +[source,console] +---- +$ fq 'matroska_path(".Segment.Tracks[0]")' file.mkv +---- + +Get path to element::: + +[source,console] +---- +$ fq 'grep_by(.id == "Tracks") | matroska_path' file.mkv +---- + +References::: + + +- https://www.rfc-editor.org/info/rfc8794 +- https://matroska.org/technical/specs/index.html +- https://www.matroska.org/technical/basics.html +- https://www.matroska.org/technical/codec_specs.html +- https://wiki.xiph.org/MatroskaOpus + +//- + +.Options +`*-o decode_samples=true*`::: + Decode samples + +-- ++ + +`*midi*`:: + Standard MIDI file ++ +-- + +Notes::: + + +* Only supports the MIDI 1.0 MIDI file specification. +* Only supports _MThd_ and _MTrk_ chunks. +* Does only basic validation on the MIDI data. + +//- + +Sample queries::: + + +* Extract the track names from a MIDI file + +//- +[source,console] +---- +fq -d midi '.. | select(.event=="track_name")? | "\(.track_name)"' midi/twinkle.mid +---- + +* Extract the tempo changes from a MIDI file + +//- +[source,console] +---- +fq -d midi '.. | select(.event=="tempo")?.tempo' midi/twinkle.mid +---- + +* Extract the key changes from a MIDI file + +//- +[source,console] +---- +fq -d midi '.. | select(.event=="key_signature")?.key_signature' midi/twinkle.mid +---- + +* Extract NoteOn events: + +//- +[source,console] +---- +fq -d midi 'grep_by(.event=="note_on") | [.time.tick, .note_on.note] | join(" ")' midi/twinkle.mid +---- + +Authors::: + + +- https://github.com/transcriptaze[transcriptaze] + +//- + +References::: + + +* https://www.midi.org/specifications/item/the-midi-1-0-specification[The Complete MIDI 1.0 Detailed Specification] +* https://midi.org/standard-midi-files[Standard MIDI Files] +* http://midi.teragonaudio.com/tech/midifile.htm[Standard MIDI File (SMF) Format] +* http://www.somascape.org/midi/tech/mfile.html[MIDI Files Specification] +* https://www.recordingblogs.com/wiki/midi-smpte-offset-meta-message[MIDI SMPTE Offset meta message] +* http://www.somascape.org/midi/tech/mfile.html#meta[Somascape MIDI Files Specification] + +//- +-- ++ + +`*moc3*`:: + MOC3 file ++ +-- + +Authors::: + + +- https://github.com/ronsor[@ronsor] + +//- +-- ++ + +`*mp3*`:: + MP3 file ++ +-- + +.Options +`*-o max_sync_seek=32768*`::: + Max byte distance to next sync + +`*-o max_unique_header_configs=5*`::: + Max number of unique frame header configs allowed + +`*-o max_unknown=50*`::: + Max percent (0-100) unknown bits + +-- ++ + +`*mp3_frame*`:: + MPEG audio layer 3 frame + +`*mp3_frame_vbri*`:: + MP3 frame Fraunhofer encoder variable bitrate tag + +`*mp3_frame_xing*`:: + MP3 frame Xing/Info tag + +`*mp4*`:: + ISOBMFF, QuickTime and similar ++ +-- + +Speed up decoding by not decoding samples::: + +[source,console] +---- +# manually decode first sample as an aac_frame +$ fq -o decode_samples=false '.tracks[0].samples[0] | aac_frame | d' file.mp4 +---- + +Entries for first edit list as values::: + +[source,console] +---- +$ fq 'first(grep_by(.type=="elst").entries) | tovalue' file.mp4 +---- + +Whole box tree as JSON (exclude mdat data and tracks)::: + +[source,console] +---- +$ fq 'del(.tracks) | grep_by(.type=="mdat").data = "" | tovalue' file.mp4 +---- + +Force decode a single box::: + +[source,console] +---- +$ fq -n '"AAAAHGVsc3QAAAAAAAAAAQAAADIAAAQAAAEAAA==" | from_base64 | mp4({force:true}) | d' +---- + +Lookup mp4 box using an mp4 box path::: + +[source,console] +---- +# | mp4_path($path) -> +$ fq 'mp4_path(".moov.trak[1]")' file.mp4 +---- + +Get mp4 box path for a decode value box::: + +[source,console] +---- +# | mp4_path -> string +$ fq 'grep_by(.type == "trak") | mp4_path' file.mp4 +---- + +References::: + + +- https://en.wikipedia.org/wiki/ISO/IEC_base_media_file_format[ISO/IEC base media file format (MPEG-4 Part 12)] +- https://developer.apple.com/standards/qtff-2001.pdf[Quicktime file format] + +//- + +.Options +`*-o allow_truncated=false*`::: + Allow box to be truncated + +`*-o decode_samples=true*`::: + Decode track samples + +`*-o skip_samples=false*`::: + Skip track samples + +-- ++ + +`*mpeg_asc*`:: + MPEG-4 Audio Specific Config + +`*mpeg_es*`:: + MPEG Elementary Stream + +`*mpeg_pes*`:: + MPEG Packetized elementary stream + +`*mpeg_pes_packet*`:: + MPEG Packetized elementary stream packet + +`*mpeg_spu*`:: + Sub Picture Unit (DVD subtitle) + +`*mpeg_ts*`:: + MPEG Transport Stream + +`*msgpack*`:: + MessagePack ++ +-- + +Convert represented value to JSON::: + +[source,console] +---- +$ fq -d msgpack torepr file.msgpack +---- + +References::: + + +- https://github.com/msgpack/msgpack/blob/master/spec.md + +//- +-- ++ + +`*negentropy*`:: + Negentropy message ++ +-- + +View a full Negentropy message::: + +[source,console] +---- +$ fq -d negentropy dd file +---- + +Or from hex::: + +[source,console] +---- +$ echo '6186b7abb47c0001108e4206828ee3bf34258465809a337c6c00019a68e37b177a50b3ae7164ccc628b962020114019c1381281c9e3849d5fbd514b7bb65ad0101e601fbf7451f5d22e7fa36ae3e910e9f5215020157014a1b26853e06e9c32eb41b1df4f9ab300201e6011840e273c84bb1344f1d4e15d9aa67920200016f12ee2340888653f10b0ec2d438ac9f0101840156d2d796f4dff004ab369b9bcfa4d81e020187013f1b3c8a019800d5764e2de6bdfd2785020114017caaf0acb5dfe249aa0f7f742402168a01018301e7b8c4decb1eae455ca5714281e3245302017a01409c22636b097362df125ddffb6d944302015b01f332208bee82acf8ed922853ee54057f020001fc3e51fdb0b92966e38017f7959903850101cc01428ce0c96d49f15b50143e4fb228cb9300000131712d30e5296a7a45d07bba452d61cd' | fq -R 'from_hex | negentropy | dd' +---- + +Check how many ranges the message has and how many of those are of 'fingerprint' mode::: + +[source,console] +---- +$ fq -d negentropy '.bounds | length as $total | map(select(.mode == "fingerprint")) | length | {$total, fingerprint: .}' message +---- + +Check get all ids in all idlists::: + +[source,console] +---- +$ fq -d negentropy '.bounds | map(select(.mode == "idlist") | .idlist | .ids) | flatten' message +---- + +Authors::: + + +- fiatjaf, https://fiatjaf.com + +//- + +References::: + + +- https://github.com/hoytech/negentropy + +//- +-- ++ + +`*nes*`:: + iNES/NES 2.0 cartridge ROM format ++ +-- + +Limitations::: + + +- `prg_rom`, `chr_rom` and `trainer` fields may contain data that is just random junk from the memory chips, since they are of a fixed size. +- The `nes_toasm` function outputs ALL opcodes, including the unofficial ones, which means that none of the regular assemblers can recompile it. +- The `nes_tokitty` function works on tiles in `chr_rom` but only outputs a Kitty graphics compatible string. You need to manually `printf` that string to get Kitty (or another compatible terminal) to output the graphics. + +//- + +Decompile PRG ROM::: + +[source,console] +---- +$ fq -r '.prg_rom[] | nes_toasm' file.nes +---- + +Print out first CHR ROM tile in Kitty (or Konsole, wayst, WezTerm) at size 5::: + +[source,console] +---- +$ printf $(fq -r -d nes '.chr_rom[0] | nes_tokitty(5)' file.nes) +---- + +Print out all CHR ROM tiles in Kitty (with Bash) at size 5::: + +[source,console] +---- +$ for line in $(fq -r '.chr_rom[] | nes_tokitty(5)' file.nes);do printf "%b%s" "$line";done +---- + +Authors::: + + +- Mikael Lofjärd mikael.lofjard@gmail.com, original author + +//- + +References::: + + +- https://www.nesdev.org/wiki/INES +- https://www.nesdev.org/wiki/NES_2.0 +- https://www.nesdev.org/wiki/CPU +- https://bugzmanov.github.io/nes_ebook/chapter_6_3.html + +//- +-- ++ + +`*ogg*`:: + OGG file + +`*ogg_page*`:: + OGG page + +`*opentimestamps*`:: + OpenTimestamps file ++ +-- + +View a full OpenTimestamps file::: + +[source,console] +---- +$ fq dd file.ots +---- + +List the names of the Calendar servers used::: + +[source,console] +---- +$ fq '.operations | map(select(.attestation_type == "calendar") | .url)' file.ots +---- + +Check if there are Bitcoin attestations present::: + +[source,console] +---- +$ fq '.operations | map(select(.attestation_type == "bitcoin")) | length > 0' file.ots +---- + +Authors::: + + +- fiatjaf, https://fiatjaf.com + +//- + +References::: + + +- https://opentimestamps.org/ +- https://github.com/opentimestamps/python-opentimestamps + +//- +-- ++ + +`*opus_packet*`:: + Opus packet + +`*pcap*`:: + PCAP packet capture ++ +-- + +Build object with number of (reassembled) TCP bytes sent to/from client IP::: + +[source,console] +---- +# for a pcapng file you would use .[0].tcp_connections for first section +$ fq '.tcp_connections | group_by(.client.ip) | map({key: .[0].client.ip, value: map(.client.stream, .server.stream | tobytes.size) | add}) | from_entries' +{ + "10.1.0.22": 15116, + "10.99.12.136": 234, + "10.99.12.150": 218 +} +---- +-- ++ + +`*pcapng*`:: + PCAPNG packet capture + +`*pg_btree*`:: + PostgreSQL btree index file ++ +-- + +Btree index meta page::: + +[source,console] +---- +$ fq -d pg_btree -o flavour=postgres14 ".[0] | d" 16404 +---- + +Btree index page::: + +[source,console] +---- +$ fq -d pg_btree -o flavour=postgres14 ".[1]" 16404 +---- + +Authors::: + + +- Pavel Safonov p.n.safonov@gmail.com https://github.com/pnsafonov[@pnsafonov] + +//- + +References::: + + +- https://www.postgresql.org/docs/current/storage-page-layout.html + +//- + +.Options +`*-o page=0*`::: + First page number in file, default is 0 + +-- ++ + +`*pg_control*`:: + PostgreSQL control file ++ +-- + +Decode content of pg_control file::: + +[source,console] +---- +$ fq -d pg_control -o flavour=postgres14 d pg_control +---- + +Specific fields can be got by request::: + +[source,console] +---- +$ fq -d pg_control -o flavour=postgres14 ".state, .check_point_copy.redo, .wal_level" pg_control +---- + +Authors::: + + +- Pavel Safonov p.n.safonov@gmail.com https://github.com/pnsafonov[@pnsafonov] + +//- + +References::: + + +- https://github.com/postgres/postgres/blob/REL_14_2/src/include/catalog/pg_control.h + +//- + +.Options +`*-o flavour=""*`::: + PostgreSQL flavour: postgres14, pgproee14, postgres10 + +-- ++ + +`*pg_heap*`:: + PostgreSQL heap file ++ +-- + +To see heap page's content::: + +[source,console] +---- +$ fq -d pg_heap -o flavour=postgres14 ".[0]" 16994 +---- + +To see page's header::: + +[source,console] +---- +$ fq -d pg_heap -o flavour=postgres14 ".[0].page_header" 16994 +---- + +First and last item pointers on first page::: + +[source,console] +---- +$ fq -d pg_heap -o flavour=postgres14 ".[0].pd_linp[0, -1]" 16994 +---- + +First and last tuple on first page::: + +[source,console] +---- +$ fq -d pg_heap -o flavour=postgres14 ".[0].tuples[0, -1]" 16994 +---- + +Authors::: + + +- Pavel Safonov p.n.safonov@gmail.com https://github.com/pnsafonov[@pnsafonov] + +//- + +References::: + + +- https://www.postgresql.org/docs/current/storage-page-layout.html + +//- + +.Options +`*-o flavour="postgres14"*`::: + PostgreSQL flavour: postgres14, pgproee14, postgres10 + +`*-o page=0*`::: + First page number in file, default is 0 + +`*-o segment=0*`::: + Segment file number (16790.1 is 1), default is 0 + +-- ++ + +`*png*`:: + Portable Network Graphics file + +`*prores_frame*`:: + Apple ProRes frame + +`*protobuf*`:: + Protobuf ++ +-- + +Can decode sub messages::: + +[source,console] +---- +$ fq -d protobuf '.fields[6].wire_value | protobuf | d' file +---- + +References::: + + +- https://developers.google.com/protocol-buffers/docs/encoding + +//- +-- ++ + +`*protobuf_widevine*`:: + Widevine protobuf + +`*pssh_playready*`:: + PlayReady PSSH + +`*rtmp*`:: + Real-Time Messaging Protocol ++ +-- +Currently only supports plain RTMP (not RTMPT or encrypted variants etc) with AMF0 (not AMF3). + +Show rtmp streams in PCAP file::: + +[source,console] +---- +fq '.tcp_connections[] | select(.server.port=="rtmp") | d' file.cap +---- + +References::: + + +- https://rtmp.veriskope.com/docs/spec/ +- https://rtmp.veriskope.com/pdf/video_file_format_spec_v10.pdf + +//- +-- ++ + +`*safetensors*`:: + SafeTensors + +`*sll2_packet*`:: + Linux cooked capture encapsulation v2 + +`*sll_packet*`:: + Linux cooked capture encapsulation + +`*stl*`:: + Stereolithography ++ +-- +Decode binary STL (Stereolithography, Standard Tesselation Language) files. + +Current limitations::: + + +* No support for ASCII STL files +* No support for VisCAM and SolidView colors +* No support for Materialise Magics colors + +//- +-- ++ + +`*tap*`:: + TAP tape format for ZX Spectrum computers ++ +-- +The TAP- (and BLK-) format is nearly a direct copy of the data that is stored in real tapes, as it is written by the ROM save routine of the ZX-Spectrum. A TAP file is simply one data block or a group of 2 or more data blocks, one followed after the other. The TAP file may be empty. +You will often find this format embedded inside the TZX tape format. +The default file extension is `.tap`. + +Processing JSON files::: + +When needing to process a generated JSON file it's recommended to convert the plain data bytes to an array by setting `bits_format=byte_array`: +[source,console] +---- +fq -o bits_format=byte_array -d tap -V d /path/to/file.tap +---- + +Authors::: + + +- Michael R. Cook work.mrc@pm.me, original author + +//- + +References::: + + +- https://worldofspectrum.net/zx-modules/fileformats/tapformat.html + +//- +-- ++ + +`*tar*`:: + Tar archive + +`*tcp_segment*`:: + Transmission control protocol segment + +`*tiff*`:: + Tag Image File Format + +`*tls*`:: + Transport layer security ++ +-- +Supports decoding of most standard records, messages and extensions. Can also decrypt most standard cipher suites in a PCAP with traffic in both directions if a NSS key log is provided. + +Decode and decrypt providing a PCAP and key log::: + +Write traffic to a PCAP file: +[source,console] +---- +$ tcpdump -i -w traffic.pcap +---- +Make sure your curl TLS backend supports `SSLKEYLOGFILE` and do: +[source,console] +---- +$ SSLKEYLOGFILE=traffic.keylog curl --tls-max 1.2 https://host/path +---- +Decode, decrypt and query. Uses `keylog=@` to read option value from keylog file: +[source,console] +---- +# decode and show whole tree +$ fq -o keylog=@traffic.keylog d traffic.pcap + +# write unencrypted server response to a file. +# first .stream is the TCP stream, second .stream is TLS application data stream +# +# first TCP connections: +$ fq -o keylog=@traffic.keylog '.tcp_connections[0].server.stream.stream | tobytes' traffic.pcap > data +# first TLS connection: +$ fq -o keylog=@traffic.keylog 'first(grep_by(.server.stream | format == "tls")).server.stream.stream | tobytes' > data +---- + +Supported cipher suites for decryption::: + +`TLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA`, `TLS_DH_ANON_EXPORT_WITH_RC4_40_MD5`, `TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA`, `TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA`, `TLS_DHE_DSS_WITH_AES_128_CBC_SHA`, `TLS_DHE_DSS_WITH_AES_128_CBC_SHA256`, `TLS_DHE_DSS_WITH_AES_128_GCM_SHA256`, `TLS_DHE_DSS_WITH_AES_256_CBC_SHA`, `TLS_DHE_DSS_WITH_AES_256_CBC_SHA256`, `TLS_DHE_DSS_WITH_AES_256_GCM_SHA384`, `TLS_DHE_DSS_WITH_DES_CBC_SHA`, `TLS_DHE_DSS_WITH_RC4_128_SHA`, `TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA`, `TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA`, `TLS_DHE_RSA_WITH_AES_128_CBC_SHA`, `TLS_DHE_RSA_WITH_AES_128_CBC_SHA256`, `TLS_DHE_RSA_WITH_AES_128_GCM_SHA256`, `TLS_DHE_RSA_WITH_AES_256_CBC_SHA`, `TLS_DHE_RSA_WITH_AES_256_CBC_SHA256`, `TLS_DHE_RSA_WITH_AES_256_GCM_SHA384`, `TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256`, `TLS_DHE_RSA_WITH_DES_CBC_SHA`, `TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA`, `TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA`, `TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256`, `TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256`, `TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA`, `TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384`, `TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384`, `TLS_ECDH_ECDSA_WITH_RC4_128_SHA`, `TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA`, `TLS_ECDH_RSA_WITH_AES_128_CBC_SHA`, `TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256`, `TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256`, `TLS_ECDH_RSA_WITH_AES_256_CBC_SHA`, `TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384`, `TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384`, `TLS_ECDH_RSA_WITH_RC4_128_SHA`, `TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA`, `TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA`, `TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256`, `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`, `TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA`, `TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384`, `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`, `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256`, `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305`, `TLS_ECDHE_ECDSA_WITH_RC4_128_SHA`, `TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA`, `TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256`, `TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA`, `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`, `TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA`, `TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256`, `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`, `TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA`, `TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384`, `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`, `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256`, `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305`, `TLS_ECDHE_RSA_WITH_RC4_128_SHA`, `TLS_PSK_WITH_AES_128_CBC_SHA`, `TLS_PSK_WITH_AES_256_CBC_SHA`, `TLS_PSK_WITH_RC4_128_SHA`, `TLS_RSA_EXPORT_WITH_DES40_CBC_SHA`, `TLS_RSA_EXPORT_WITH_RC4_40_MD5`, `TLS_RSA_WITH_3DES_EDE_CBC_SHA`, `TLS_RSA_WITH_AES_128_CBC_SHA`, `TLS_RSA_WITH_AES_128_CBC_SHA256`, `TLS_RSA_WITH_AES_128_GCM_SHA256`, `TLS_RSA_WITH_AES_256_CBC_SHA`, `TLS_RSA_WITH_AES_256_CBC_SHA256`, `TLS_RSA_WITH_AES_256_GCM_SHA384`, `TLS_RSA_WITH_DES_CBC_SHA`, `TLS_RSA_WITH_RC4_128_MD5`, `TLS_RSA_WITH_RC4_128_SHA` + +References::: + + +- https://www.rfc-editor.org/rfc/rfc5246[RFC 5246: The Transport Layer Security (TLS) Protocol] +- https://www.rfc-editor.org/rfc/rfc6101[RFC 6101: The Secure Sockets Layer (SSL) Protocol Version 3.0] + +//- + +.Options +`*-o keylog=""*`::: + NSS Key Log content + +-- ++ + +`*toml*`:: + Tom's Obvious, Minimal Language + +`*tzif*`:: + Time Zone Information Format ++ +-- + +Get last transition time::: + +[source,console] +---- +fq '.v2plusdatablock.transition_times[-1] | tovalue' tziffile +---- + +Count leap second records::: + +[source,console] +---- +fq '.v2plusdatablock.leap_second_records | length' tziffile +---- + +Authors::: + + +- Takashi Oguma https://github.com/bitbears-dev[@bitbears-dev] https://twitter.com/0xb17bea125[@0xb17bea125] + +//- + +References::: + + +- https://datatracker.ietf.org/doc/html/rfc8536 + +//- +-- ++ + +`*tzx*`:: + TZX tape format for ZX Spectrum computers ++ +-- +`TZX` is a file format designed to preserve cassette tapes compatible with the ZX Spectrum computers, although some specialized versions of the format have been defined for other machines such as the Amstrad CPC and C64. +The format was originally created by Tomaz Kac, who was the maintainer until `revision 1.13`, before passing it to Martijn v.d. Heide. For a brief period the company Ramsoft became the maintainers, and created revision `v1.20`. +The default file extension is `.tzx`. + +Processing JSON files::: + +When needing to process a generated JSON file it's recommended to convert the plain data bytes to an array by setting `bits_format=byte_array`: +[source,console] +---- +fq -o bits_format=byte_array -d tzx -V d /path/to/file.tzx +---- + +Authors::: + + +- Michael R. Cook work.mrc@pm.me, original author + +//- + +References::: + + +- https://worldofspectrum.net/TZXformat.html + +//- +-- ++ + +`*udp_datagram*`:: + User datagram protocol + +`*vorbis_comment*`:: + Vorbis comment + +`*vorbis_packet*`:: + Vorbis packet + +`*vp8_frame*`:: + VP8 frame + +`*vp9_cfm*`:: + VP9 Codec Feature Metadata + +`*vp9_frame*`:: + VP9 frame + +`*vpx_ccr*`:: + VPX Codec Configuration Record + +`*wasm*`:: + WebAssembly Binary Format ++ +-- + +Count opcode usage::: + +[source,console] +---- +$ fq '.sections[] | select(.id == "code_section") | [.. | .opcode? // empty] | count | map({key: .[0], value: .[1]}) | from_entries' file.wasm +---- + +List exports and imports::: + +[source,console] +---- +$ fq '.sections | {import: map(select(.id == "import_section").content.im.x[].nm.b), export: map(select(.id == "export_section").content.ex.x[].nm.b)}' file.wasm +---- + +Authors::: + + +- Takashi Oguma https://github.com/bitbears-dev[@bitbears-dev] https://twitter.com/0xb17bea125[@0xb17bea125] + +//- + +References::: + + +- https://webassembly.github.io/spec/core/ + +//- +-- ++ + +`*wav*`:: + WAV file + +`*webp*`:: + WebP image + +`*xml*`:: + Extensible Markup Language ++ +-- +XML can be decoded and encoded into jq values in two ways, elements as object or array. The object variant might be easier to query for a specific value but array might be easier to use to generate xml or to query after all elements of some kind etc. +Encoding is done using the `to_xml` function and it will figure what variant that is used based on the input value. It has two optional options `indent` and `attribute_prefix`. + +Elements as object::: + +Element can have different shapes depending on body text, attributes and children: + +- `text` is `{"a":{"#text":"text","@key":"value"}}`, has text (`#text`) and attributes (`@key`) +- `text` is `{"a":"text"}` +- `text` is `{"a":{"b":"text"}}` one child with only text and no attributes +- `text` is `{"a":{"b":["","text"]}}` two children with same name end up in an array +- `text` is `{"a":{"b":["",{"#text":"text","@key":"value"}]}}` + +//- +If there is `#seq` attribute it encodes the child element order. Use `-o seq=true` to include sequence number when decoding, otherwise order might be lost. +[source,console] +---- +# decode as object is the default +$ echo 'bbbccc' | fq -d xml -o seq=true +{ + "a": { + "b": [ + { + "#seq": 0 + }, + { + "#seq": 1, + "#text": "bbb" + } + ], + "c": { + "#seq": 2, + "#text": "ccc", + "@attr": "value" + } + } +} + +# access text of the element +$ echo 'bbbccc' | fq '.a.c["#text"]' +"ccc" + +# decode to object and encode to xml +$ echo 'bbbccc' | fq -r -d xml -o seq=true 'to_xml({indent:2})' + + + bbb + ccc + +---- + +Elements as array::: + +Elements are arrays of the shape `["name", null|{#text|attribute: "value"}, [, ...]]`. +[source,console] +---- +# decode as array +$ echo 'bbbccc' | fq -d xml -o array=true +[ + "a", + null, + [ + [ + "b", + null, + [] + ], + [ + "b", + { + "#text": "bbb" + }, + [] + ], + [ + "c", + { + "#text": "ccc", + "attr": "value" + }, + [] + ] + ] +] + +# decode to array and encode to xml +$ echo 'bbbccc' | fq -r -d xml -o array=true -o seq=true 'to_xml({indent:2})' + + + bbb + ccc + + +# access text of the element, the object variant above is probably easier to use +$ echo 'bbbccc' | fq -o array=true '.[2][2][1]["#text"]' +"ccc" +---- + +References::: + + +- https://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html[xml.com's Converting Between XML and JSON] + +//- + +.Options +`*-o array=false*`::: + Decode as nested arrays + +`*-o attribute_prefix="@"*`::: + Prefix for attribute keys + +`*-o seq=false*`::: + Use seq attribute to preserve element order + +-- ++ + +`*yaml*`:: + YAML Ain't Markup Language + +`*zip*`:: + ZIP archive ++ +-- +Supports ZIP64. + +Timestamp and time zones::: + +The timestamp accessed via `.local_files[].last_modification` is encoded in ZIP files using https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-dosdatetimetovarianttime[MS-DOS representation] which lacks a known time zone. Probably the local time/date was used at creation. The `unix_guess` field in `last_modification` is a guess assuming the local time zone was UTC at creation. + +References::: + + +- https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT +- https://opensource.apple.com/source/zip/zip-6/unzip/unzip/proginfo/extra.fld +- https://formats.kaitai.io/dos_datetime/ +- https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-dosdatetimetovarianttime + +//- + +.Options +`*-o uncompress=true*`::: + Uncompress and probe files + +-- ++ + + +== Encodings, serializations and hashes + +In addition to binary formats fq also supports various encodings and serialization formats. + +At the moment fq does not have any dedicated argument for serialization formats but raw string input `-R` slurp `-s` and raw string output `-r` can make things easier. The combination `-Rs` will read all inputs into one string (same as jq). + +Note that `from*` functions output jq values and `to*` functions take jq values as input so in some cases not all information will be properly preserved. For example, the element and attribute order might change and text and comment nodes might move or be merged. https://github.com/mikefarah/yq[yq] might be a better tool if that is needed. + +Some example usages: + +[source,console] +---- +# read yml (format is probed, use -d yaml to force) and do some query +$ fq '...' file.yml + +# convert YAML to JSON +# note the -r for raw string output, without it a JSON string with escaped JSON would be output +$ fq -r 'tojson({indent:2})' file.yml + +# add token to URL +$ echo -n "https://host.org" | fq -Rsr 'from_url | .user.username="token" | to_url' +https://token@host.org + +# top 3 hosts in src or href attributes: +# -d to decode as html, can't be probed as html5 parsers always produce some parse tree +# [...] to start collect values into an array +# .. | ."@src"?, ."@href"? | values, recurse and try (?) to get src and href attributes and filter out nulls +# from_url.host | values, parse as url and filter out those without a host +# count to count unique values, returns [[key, count], ...] +# reverse sort by count and pick first 3 +# map [key, count] tuples into {key: key, value: count} +# from_entries, convert into object +$ curl -s https://www.discogs.com/ | fq -d html '[.. | ."@src"?, ."@href"? | values | from_url.host | values] | count | sort_by(-.[1])[0:3] | map({key: .[0], value: .[1]}) | from_entries' +{ + "blog.discogs.com": 9, + "st.discogs.com": 10, + "www.discogs.com": 14 +} + +# shows how serialization functions can be used on any string, how to transform values and output some other format +# read and decode zip file and start an interactive REPL +$ fq -i . <(curl -sL https://github.com/stefangabos/world_countries/archive/master.zip) +# select from interesting xml file +zip> .local_files[] | select(.file_name == "world_countries-master/data/countries/en/world.xml").uncompressed | repl +# convert xml into jq value +> .local_files[95].uncompressed string> from_xml | repl +# sort countries by and select the first one +>> object> .countries.country | sort_by(."@name") | first | repl +# see what current input is +>>> object> . +{ + "@alpha2": "af", + "@alpha3": "afg", + "@id": "4", + "@name": "Afghanistan" +} +# remove "@" prefix from keys and convert to YAML and print it +>>> object> with_entries(.key |= .[1:]) | to_yaml | print +alpha2: af +alpha3: afg +id: "4" +name: Afghanistan +# exit all REPLs back to shell +>>> object> ^D +>> object> ^D +> .local_files[95].uncompressed string> ^D +zip> ^D +---- + +=== XML and HTML + +* `from_xml`/`from_xml($opts)` Parse XML into jq value. `$opts` are: +** `{seq: true}` preserve element ordering if more than one sibling. + +** `{array: true}` use nested `[name, attributes, children]` arrays to represent elements. Attributes will be `null` if none and children will be `[]` if none, this is to make it easier to work with as the array always has 3 values. `to_xml` does not require this. + +* `from_html`/`from_html($opts)` Parse HTML into jq value. + + Similar to `from_xml` but parses html5 in non-script mode. Will always have a `html` root with `head` and `body` elements. + + + `$opts` are: +** `{array: true}` use nested arrays to represent elements. + +** `{seq: true}` preserve element ordering if more than one sibling. + +* `to_xml`/`to_xml($opts)` Serialize jq value into XML. + + Assumes object representation if input is an object, and nested arrays if input is an array. + + Will automatically add a root `doc` element if jq value has more than one root element. + + If a `#seq` is found on at least one element all siblings will be sorted by sequence number. Attributes are always sorted. + + + `$opts` are: +** `{indent: number}` indent child elements. + +XML elements can be represented as jq value in two ways, as objects (inspired by https://github.com/clbanning/mxj[mxj] and https://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html[xml.com's Converting Between XML and JSON]) or nested arrays. Both representations are lossy and might lose ordering of elements, text nodes and comments. In object representation `from_xml`, `from_html` and `to_xml` support `{seq: true}` option to parse/serialize `{"#seq": }` attributes to preserve element sibling ordering. + +The object version is denser and convenient to query, the nested arrays version is probably easier to use when generating XML. + +Let's assume `$xml` is this XML document as a string: +[source,xml] +---- + + + text + text + +---- + +With object representation an element is represented as: + +** Attributes as `@` prefixed `@` keys. +** Text nodes as `#text`. +** Comment nodes as `#comment` keys. +** For explicit sibling ordering `#seq` keys with a number, can be negative, assumed zero if missing. +** Child element with only text as `` key with text as value. +** Child element with more than just text as `` key with value an object. +** Multiple child element siblings with same name as `` key with value as array with strings and objects. + +[source,console] +---- +> $xml | from_xml +{ + "doc": { + "child": [ + { + "@attr": "1" + }, + { + "#text": "text", + "@attr": "2" + } + ], + "other": "text" + } +} +---- + +With nested array representation, an array with these values `["", {attributes...}, [children...]]`. + +- Index 0 is an element name. +- Index 1 object attributes (including `#text` and `#comment` keys). +- Index 2 array of child elements. + +[source,console] +---- +> $xml | from_xml({array: true}) +[ + "doc", + null, + [ + [ + "child", + { + "attr": "1" + }, + [] + ], + [ + "child", + { + "#text": "text", + "attr": "2" + }, + [] + ], + [ + "other", + { + "#text": "text" + }, + [] + ] + ] +] +---- + +Parse and include `#seq` attributes if needed: + +[source,console] +---- +> $xml | from_xml({seq:true}) +{ + "doc": { + "child": [ + { + "#seq": 0, + "@attr": "1" + }, + { + "#seq": 1, + "#text": "text", + "@attr": "2" + } + ], + "other": { + "#seq": 2, + "#text": "text" + } + } +} +---- + +Select values in ``, remove ``, add a `` element, serialize to xml with 2 space indent and print the string + +[source,console] +---- +> $xml | from_xml.doc | del(.child) | .new = "abc" | {root: .} | to_xml({indent: 2}) | println + + abc + text + +---- + +=== JSON + +* `fromjson` Parse JSON into jq value. +* `tojson`/`tojson($opts)` Serialize jq value into JSON. `$opts` are: +** `{indent: number}` Indent depth. +* `from_jsonl` Parse JSON lines into jq array. +* `to_jsonl` Serialize jq array into JSONL. + +=== jq-flavoured JSON + +* `from_jq` Parse jq-flavoured JSON into jq value. +* `to_jq`/`to_jq($opts)` Serialize jq value into jq-flavoured JSON. jq-flavoured JSON has optional key quotes, `#` comments and can have trailing comma in objects. `$opts` are: +** `{indent: number}` Indent depth. + +Note that `fromjson` and `tojson` use different naming conventions as they originate from jq's standard library. + +=== YAML + +* `from_yaml` Parse YAML into jq value. +* `to_yaml`/`to_yaml($opts)` Serialize jq value into YAML. `$opts` are: +** `{indent: number}` Indent depth. + +=== TOML + +* `from_toml` Parse TOML into jq value. +* `to_toml`/`to_toml($opts)` Serialize jq value into TOML. `$opts` are: +** `{indent: number}` Indent depth. + +=== CSV + +* `from_csv`/`from_csv($opts)` Parse CSV into jq value. + + To work with tab separated values you can use `from_csv({comma: "\t"})` or `fq -d csv -o 'comma="\t"'`. + + `$opts` are: +** `{comma: string}` field separator, default ",". + +** `{comment: string}` comment line character, default "#". + +* `to_csv`/`to_csv($opts)` Serialize jq value into CSV. `$opts` are: +** `{comma: string}` field separator, default ",". + + +=== XML entities + +- `from_xmlentities` Decode XML entities. +- `to_xmlentities` Encode XML entities. + +=== URL + +- `from_urlpath` Decode URL path component. +- `to_urlpath` Encode URL path component. Whitespace as %20. +- `from_urlencode` Decode URL query encoding. +- `to_urlencode` Encode URL to query encoding. Whitespace as "+". +- `from_urlquery` Decode URL query into object. For duplicate keys value will be an array. +- `to_urlquery` Encode object into query string. +- `from_url` Decode URL into object. ++ +-- +[source,console] +---- +> "schema://user:pass@host/path?key=value#fragment" | from_url +{ + "fragment": "fragment", + "host": "host", + "path": "/path", + "query": { + "key": "value" + }, + "rawquery": "key=value", + "scheme": "schema", + "user": { + "password": "pass", + "username": "user" + } +} +---- +-- +- `to_url` Encode object into URL string. + +=== Hex and base64 + +- `from_hex` Decode hex string to binary. +- `to_hex` Encode binary into hex string. +- `from_base64`/`from_base64($opts)` Decode base64 encodings into binary. `$opts` are: + - `{encoding:string}` encoding variant: `std` (default), `url`, `rawstd` or `rawurl` +- `to_base64`/`to_base64($opts)` Encode binary into base64 encodings. `$opts` are: + - `{encoding:string}` encoding variant: `std` (default), `url`, `rawstd` or `rawurl` + +=== Hash functions + +- `to_md4` Hash binary using md4. +- `to_md5` Hash binary using md5. +- `to_sha1` Hash binary using sha1. +- `to_sha256` Hash binary using sha256. +- `to_sha512` Hash binary using sha512. +- `to_sha3_224` Hash binary using sha3 224. +- `to_sha3_256` Hash binary using sha3 256. +- `to_sha3_384` Hash binary using sha3 384. +- `to_sha3_512` Hash binary using sha3 512. + +=== Text encodings + +- `to_iso8859_1` Encode string as ISO8859-1 into binary. +- `from_iso8859_1` Decode binary as ISO8859-1 into string. +- `to_utf8` Encode string as UTF8 into binary. +- `from_utf8` Decode binary as UTF8 into string. +- `to_utf16` Encode string as UTF16 into binary. +- `from_utf16` Decode binary as UTF16 into string. +- `to_utf16le` Encode string as UTF16 little-endian into binary. +- `from_utf16le` Decode binary as UTF16 little-endian into string. +- `to_utf16be` Encode string as UTF16 big-endian into binary. +- `from_utf16be` Decode binary as UTF16 big-endian into string. + +== Interactive REPL + +The REPL can be useful in some scenarios: + +- When decoding is slow you can reuse the decode result. +- Dig thru a file using sub-REPL to cut down on typing. +- Use auto-completion to speed up typing. + +[source,console] +---- +# start REPL with no (null) input +$ fq -i +null> +# same as +$ fq -ni +null> + +# in the REPL you will see a prompt indicating current input and you can type a jq expression to evaluate. + +# start REPL with one file as input +$ fq -i . doc/file.mp3 +mp3> +# basic arithmetic and jq expressions +mp3> 1+1 +2 +mp3> 1, 2, 3 | . * 2 +2 +4 +6 +mp3> [1, 2, 3] | add +6 +# "." is the identity function which just returns current input, the mp3 file. +mp3> . +# access the first frame in the mp3 file +mp3> .frames[0] +# start a new nested REPL with first frame as input +mp3> .frames[0] | repl +# prompt shows "path" to current input and that it's an mp3_frame. +# Ctrl-D to exit REPL or to shell if last REPL +> .frames[0] mp3_frame> ^D +# "jq" value of layer in first frame +mp3> .frames[0].header.layer | tovalue +3 +mp3> .frames[0].header.layer * 2 +6 +# symbolic value, same as "jq" value +mp3> .frames[0].header.layer | tosym +3 +# actual underlying decoded value +mp3> .frames[0].header.layer | toactual +1 +# description of value +mp3> .frames[0].header.layer | todescription +"MPEG Layer 3" +mp3> ^D +$ +---- + +Use Ctrl-D to exit and Ctrl-C to interrupt current evaluation. + +== Examples + +=== Basic usage + +fq tries to behave the same way as jq as much as possible, so you can do: + +[source,console] +---- +fq . file +fq < file +cat file | fq +fq . < file +fq . *.png *.mp3 +fq '.frames[0]' *.mp3 +fq '.frames[-1] | tobytes' file.mp3 > last_frame +---- + +=== Common usages + +[source,console] +---- +# recursively display decode tree but truncate long arrays +fq d file +# same as +fq display file + +# display all bytes for each value +fq dd file +# same as +fq 'd({array_truncate: 0, string_truncate: 0, display_bytes: 0})' file + +# display 200 bytes for each value +fq 'd({display_bytes: 200})' file + +# recursively display decode tree without truncating +fq da file +# same as +fq 'd({array_truncate: 0, string_truncate: 0})' file + +# display a specific decode tree one level +fq '.path[1].to.value' file +# display a specific decode tree all levels +fq '.path[1].to.value | d' file +fq '.path[1].to.value | dd' file +fq '.path[1].to.value | da' file + +# recursively and verbosely display decode tree +fq dv file +# same as +fq 'd({array_truncate: 0, string_truncate: 0, verbose: true})' file + +# JSON representation for whole file +fq tovalue file +# or use -V (--value-output) that does tovalue automatically +fq -V . file +# or -Vr if the value is a string and you want a "raw" string +fq -Vr .path.to.string file +# JSON but raw bit fields truncated +fq -o bits_format=truncate tovalue file +# JSON but raw bit fields as md5 hex string +fq -o bits_format=md5 tovalue file +# JSON but raw bit fields as byte arrays +fq -o bits_format=byte_array tovalue file +# look up a path +fq '.some[1].path' file +# look up a path and output JSON +fq -V '.some[1].path' file +# can be a query that outputs multiple values +# this outputs first and last value in .some array and .path, three values in total +fq -V '.some[0,-1], .path' file + +# grep whole tree by value +fq 'grep("^prefix")' file +fq 'grep(123)' file +# grep whole tree by condition +fq 'grep_by(. >= 100 and . <= 100)' file + +# recursively look for values fulfilling some condition +fq '.. | select(.type=="trak")?' file +fq 'grep_by(.type=="trak")' file +# grep_by(f) is an alias for .. | select(f)?, that is: recurse, select and ignore errors + +# recursively look for decode value roots for a format +fq '.. | select(format=="jpeg")' file +# can also use grep_by +fq 'grep_by(format=="jpeg")' file + +# recursively look for first decode value root for a format +fq 'first(.. | select(format=="jpeg"))' file +fq 'first(grep_by(format=="jpeg"))' file + +# decode file as mp4 and return a result even if there are some errors +fq -d mp4 file.mp4 +# decode file as mp4 and also ignore validity assertions +fq -o force=true -d mp4 file.mp4 +---- + diff --git a/doc/fq.1.tmpl.adoc b/doc/fq.1.tmpl.adoc new file mode 100644 index 000000000..2dc0e6c8d --- /dev/null +++ b/doc/fq.1.tmpl.adoc @@ -0,0 +1,994 @@ += fq(1) +Mattias Wadman +v$FQ_VERSION +:doctype: manpage +:manmanual: FQ +:mansource: FQ +:toc: left +:toclevels: 3 +:sectanchors: +:reproducible: + +== Name + +fq - tool, language and decoders for working with binary formats + +== Synopsis + +`*fq [_OPTIONS_] [--] [_EXPRESSION_] [_FILE_...]*` + +== Description + +**fq** is a tool, language, and decoders for working with binary formats and data. +In most cases it behaves and feels similar to https://jqlang.github.io/jq/[jq] and it +also uses the same expression language. +To get the most out of fq it's recommended to learn more about jq. + +It features a structural hex viewer, nested format decoding, slicing and concatenating +binary data, bit-level decoding and an interactive REPL with auto-completion. + +[source,console] +---- +# Evaluate "d" for file.mp4 +$ fq d file.mp4 + +# Evaluate ".boxes[0].type" for all *.mp4 files +$ fq '.boxes[0].type' *.mp4 + +# Evaluate "1+2" without reading any input +$ fq -n 1+2 +---- + +For more advanced usage see `--arg`, `--slurp`, `--raw-file`, `-n` etc and examples at the end of the documentation. + +== Options + +////jq-eval +( _opt_cli_opts +| to_entries +| sort_by(.value.long) +| .[] +| .value as $v +| ([$v.long, $v.short | select(. != null)] | join("*`, `*") ) as $flags +| if $v.bool then + "`*\($flags)*`::" + elif $v.pairs then + "`*\($flags) \($v.pairs)*`::" + elif $v.string then + "`*\($flags) \($v.string)*`::" + elif $v.array then + "`*\($flags) \($v.array)*`::" + elif $v.object then + "`*\($flags) \($v.object)*`::" + elif $v.positional then + "`*\($flags)*`::" + else + error("unknown option type \($v)") + end +, " \($v.description)" +, ( select($v.long == "--option" ) + | ( def _type_to_human: + { "boolean": "true|false" + , "csv_ranges_array": "ranges=string,..." + , "csv_kv_obj": "key=value,..." + }[.] // .; + ( _opt_options + | to_entries + | sort_by(.key) + | .[] + | . as $opt + | select(.value.internal | not) + | "`*-o \(.key)=\(.value.type | _type_to_human)*`:::" + , " \(.value.description)" + , if .value.values then + ( .value.values + | to_entries + | sort_by(.key) + | .[] + | ( "`*-o \($opt.key)=\(.key)*`::::" + , " \(.value)" + ) + ) + else empty + end + , "" + ) + ) + ) +, "" +) +////jq-eval + +== Configuration + +=== Init files + +To add your own functions you can use `init.jq` that is read from: + +macOS:: + `$HOME/Library/Application Support/fq/init.jq` or + + `$HOME/.config/fq/init.jq` +Linux and BSD:: + `$HOME/.config/fq/init.jq` +Windows:: + `%AppData%\fq\init.jq` + +=== Environment + +`*NO_COLOR*`:: + Non-empty string disables color output. +`*CLIUNICODE*`:: + Enables use of unicode output characters. +`*COMPLETION_TIMEOUT*`:: + REPL completion timeout in seconds. +`*NO_DECODE_PROGRESS*`:: + Disables decode progress indicator. + +== Expression + +=== Syntax + +See *jq*(1) for syntax details. But here are some common beginner gotchas: + +- Functions that take no arguments are called using `name` instead of `name()`. +- Arguments are separated by `;` instead of `,`. Comma is used to concatenate output streams. +To call a function `f` with two arguments use `f(1; 2)`. If you do `f(1, 2)` +you pass a single argument `1, 2`, a filter that outputs `1` and then `2`, to `f`. +- Expressions can return or "output" zero or more values. This is how iteration etc is done, `1, 2` outputs `1` then `2`. +- Similar to shell pipelines, implicit input and output are used and piped together using `|`. `.` is used to refer to +current input. Ex `1 | . + 2` outputs `3`, `1, 2 | . + 2` outputs `3` and `4`. +- In the jq manual and other jq related documentation you might see `name/2`, this means the +function `name` takes two arguments (arity). + +=== Additional features + +fq uses an extended variant of the jq language with a few extra features: + +* Arbitrary-precision integers and arithmetics. +* Supports raw strings using back-ticks. String interpolation and codepoint escapes are not processed. ++ +---- +`hello \("world")\ud83c\udf0d` +---- +results in the string `hello \("world")\ud83c\udf0d` or as JSON `"hello \\(\"world\")\\ud83c\\udf0d"`. +In contrast ++ +---- +"hello \("world")\ud83c\udf0d" +---- +results in the string `hello world🌍` or as JSON `"hello world🌍"`. ++ +* Supports more number bases in integer literals. +** `0xabcd` (Hexadecimal). +** `0o125715` (Octal). +** `0b1010101111001101` (Binary). +** Grouping using underscore `0xab_cd`. +* Binary and decode value types, see below. +* Try include using an ending question mark `include "file?";` that doesn't fail if file is missing or has errors. +* Some values can act as an object with keys even when they are arrays, numbers etc. +* There can be keys hidden from `keys` and `[]`. +* Some values are readonly and can't be updated or will convert to JSON on update. +* Mixing `--args` and `--jsonargs` does not behave the same. + +=== Additional functions + +`*band*`, `*bor*`, `*bxor*`, `*bsl*`, `*bsr*`, `*bnot*`:: + Bitwise operations as functions. Works the same as jq's math functions. Functions that take one argument use + input, `1 | bnot`, and functions with more arguments ignore the input and use formal arguments `bsl(1; 3)`. + +`*chunk($size)*`:: + Split array or string into `$size` length chunks. Last chunk might be shorter. + +`*count*`, `*count_by(f)*`:: + Like `group` but outputs array of `[value, count]` pairs. + +`*delta*`, `*delta_by(f)*`:: + Array with difference between consecutive. `delta` is the same as `delta_by(.b - .a)`. + +`*diff($a; $b)*`:: + Produce a diff between `$a` and `$b`. Differences are represented as an object `{a: , b: }`. + +`*expr_to_path*`:: + Converts from a string `".key[1]"` to a path value `["key", 1]`. + +`*grep_by(f)*`:: + Recursively select using a filter and ignore any errors. + Ex: `grep_by(. > 180 and . < 200)`, `first(grep_by(format == "id3v2"))`. + This is the same as doing `.. | select(f)?`. + +`*group*`:: + Group values, same as `group_by(.)`. + +`*path_to_expr*`:: + Converts a path value `["key", 1]` to a string `".key[1]"`. + +`*paste*`:: + Read string from stdin until ^D. Useful for pasting text. Ex: `paste | from_pem | asn1_ber | repl` read from stdin then decode and start a new sub-REPL with result. + +`*streaks*`, `*streaks_by(f)*`:: + Like `group` but groups streaks based on condition. + +`*repl*`, `*repl($opts)*`:: + Nested REPL. Must be last in a pipeline. `repl` can "slurp" outputs, ex: `1, 2, 3 | repl`, and supports options, ex: `[1,2,3] | repl({compact: true})`. + +`*slurp("")*`:: + Slurp outputs and saves them to `$name`. Must be last in the pipeline. Will be available as a global array `$name`. Ex `1,2,3 | slurp("a")`, `$a[]` same as `spew("a")`. + +`*spew*`, `*spew("")*`:: + Outputs all or a specific slurp. Ex: `spew("a")`. + +`*println*`, `*print*`:: +Print string or compact JSON to stdout with and without new line. + +`*printerrln*`, `*printerr*`:: +Print string or compact JSON to stderr with and without new line. + +=== Decode value + +A decode value is the type returned from decoding a format and used to represent values produced by a decoder. +It can be seen as representing any standard jq type but with some additional properties attached. + +Each decode value has these properties: + +* Bit range in the input. Can be used as a binary using `tobytes`, `tobytesrange`, `tobits` and `tobitsrange`. +* If scalar type, an actual value: +** This is the decoded representation of the bits, a number, string, bool etc. +** Can be accessed using `toactual`. +* If scalar type, an optional symbolic value: +** Is usually a mapping of the actual to symbolic value, ex: map number to a string value. +** Can be accessed using `tosym`. +* An optional description: +** Can be accessed using `todescription`. +* `parent` is the parent decode value +* `parents` is all the parent decode values +* `topath` is the jq path for the decode value +* `torepr` converts decode value to its representation if possible + +The value of a decode value is the symbolic value if available and otherwise the actual value. To explicitly access the value use `tovalue`. In most expressions this is not needed as it will be done automatically. + +==== Decode value functions + +`*root*`:: + Root decode value for decode value. + +`*buffer_root*`:: + Root decode value of sub buffer for decode value. + +`*format_root*`:: + Root decode value of nested format for decode value. + +`*parent*`:: + Parent decode value for decode value. + +`*parents*`:: + Outputs all parent decode values from decode value. + +`*topath*`:: + Path for decode value. Use `path_to_expr` to get a string representation. + +`*tovalue`, `tovalue($opts)*`:: + Symbolic, if available, or actual value for decode value. + +`*toactual`, `toactual($opts)*`:: + Actual value for decode value. + +`*tosym`, `tosym($opts)*`:: + Symbolic value for decode value. + +`*todescription*`:: + Description for decode value. + +`*torepr*`:: + Converts decode value into what it represents. For example converts msgpack decode value into a value representing its JSON representation. + +`tobytes`, `tobytesrange`, `tobits` and `tobitsrange` on a decode value will return the raw source bits as a binary. + +=== Binary + +Binary type is used to store raw bits or bytes. Raw bits will act as zero bits padded strings in standard jq expressions. + +Use `tobits` and `tobytes` to create them from decode value, string, number or binary array. `tobytes` will if needed zero pad most significant bits to be byte aligned. + +There is also `tobitsrange` and `tobytesrange` which do the same thing but will preserve source range when displayed. + +- `"string" | tobytes` produces a binary with UTF8 bytes. +- `1234 | tobits` produces a binary with the unsigned big-endian integer 1234 with enough bits to represent the number. Use `tobytes` to get the same but with enough bytes to represent the number. This is different to how numbers work inside binary arrays where they are limited to 0-255. +- `["abc", 123, ...] | tobytes` produces a binary from a binary array. See <> below. +- `.[index]` access bit or byte at index `index`. Index is in units. + - `[0x12, 0x34, 0x56] | tobytes[1]` is `0x34` + - `[0x12, 0x34, 0x56] | tobits[3]` is `1` +- `.[start:]`, `.[start:end]` or `.[:end]` is normal jq slice syntax and will slice the binary from `start` to `end`. `start` and `end` are in units. + - `[0x12, 0x34, 0x56] | tobytes[1:2]` will be a binary with the byte `0x34` + - `[0x12, 0x34, 0x56] | tobits[4:12]` will be a binary with the byte `0x23` + - `[0x12, 0x34, 0x56] | tobits[4:20]` will be a binary with the bytes `0x23`, `0x45` + - `[0x12, 0x34, 0x56] | tobits[4:20] | tobytes[1:]` will be a binary with the byte `0x45` + - Both `.[index]` and `.[start:end]` support negative indices to index from end. +- `explode` outputs an array with all bytes or bits as integers. + +==== Binary functions + +`*grep($v)*`, `*grep($v; $flags)*`, `*vgrep($v)*`, `*vgrep($v; $flags)*`, `*bgrep($v)*`, `*bgrep($v; $flags)*`:: + Recursively match `$v`. + `$v` is a scalar to match, where a string is treated as a regexp. A binary will match exact bytes. + `$flags` arguments are regexp flags with additional flag "b" that will treat each byte in the input binary + as a code point. This makes it possible to match exact bytes. + +`*fgrep($v)*`, `*fgrep($v; $flags)*`:: + Recursively match field name in a decode value. + +`*tobits*`:: + Transform input to binary with bit as unit and don't preserve source range. + +`*tobitsrange*`:: + Transform input to binary with bit as unit and preserve source range. + +`*tobytes*`:: + Transform input to binary with byte as unit and don't preserve source range. + +`*tobytesrange*`:: + Transform input to binary with byte as unit and preserve source range. + +`*open*`:: + Open file for reading. + +=== Binary array [[binary_array]] + +Binary array is a value "shape" and not a new type. It's an array of numbers, strings, binaries or other +binary arrays. They can be used as input to `tobits`, `tobytes` or other function that accept a binary as input. + +- Number is a byte with value 0-255 +- String as UTF8 bytes +- Binary as is +- Binary array used recursively + +Binary arrays are similar to and inspired by https://www.erlang.org/doc/man/erlang.html#type-iolist[Erlang iolist]. + +Some examples: + +- `[0, 123, 255] | tobytes` will be binary with 3 bytes 0, 123 and 255. +- `[0, [123, 255]] | tobytes` same as above. +- `[0, 1, 1, 0, 0, 1, 1, 0 | tobits] | tobytes` will be binary with 1 byte, 0x66. +- `[(.a | tobytes[-10:]), 255, (.b | tobits[:10])] | tobytes` the concatenation of the last 10 bytes of `.a`, byte of value 255 and the first 10 bits of `.b`. + +=== Differences to jq + +See https://github.com/itchyny/gojq#difference-to-jq[gojq's differences to jq]. + +=== Naming inconsistencies + +jq's naming convention is a bit inconsistent. Some standard library functions are named `tojson` while others `from_entries`. fq follows this tradition but tries to use `snake_case` unless there is a good reason. + +Here are all the non-snake_case functions added by fq. Most of them deal with decode and binary values which are new "primitive" types: + +- `toactual` +- `tobits` +- `tobitsrange` +- `tobytes` +- `tobytesrange` +- `todescription` +- `topath` +- `torepr` +- `tosym` +- `tovalue` + +== Display output + +`display` or `d` is the main function for displaying values and is also the function that will be used if no other output function is explicitly used. If its input is a decode value it will output a dump and tree structure or otherwise it will output as JSON. + +Below demonstrates some usages: + +The first and second examples do the same thing, inputting `"hello"` to `display`. + +image::display_json.svg[] + +In the next few examples we select out the first "edit list" box in an mp4 file and display it in various ways. + +By default, display will only show the root level: + +image::display_decode_value.svg[] + +First row shows a ruler with byte offset into the line and jq path for the value. + +The columns are: + +- Start address for the line. For example we see that `size` starts at `0xd5c` (row) + `0x09` (column) = `0xd65`. +- Hex representation of input bits for value. Will show the whole byte even if the value only partially uses bits from it. +- ASCII representation of input bits for value. Will show the whole byte even if the value only partially uses bits from it. +- Tree structure of decoded value, symbolic value and description. + +Notation: + +- `{}` value is an object that might have nested values. +- `[start:end]` value is an array with index starting at `start` and ending at `end` (exclusive). + +With `display` or `d` it will recursively show the whole tree: + +image::display_decode_value_d.svg[] + +Same but verbose `dv`: + +image::display_decode_value_dv.svg[] + +In verbose mode bit ranges and array element names are shown. + +Bit ranges use `[.]-[.]` as notation where `.` is left out if byte aligned. For example `type` starts at byte `0xd69` bit `0` (`.0` is left out) and ends at `0xd6d` bit `0` (exclusive) and has a size of `4` bytes. + +This verbosely displays the header of the second frame in an mp3 file which has a bunch of non-byte-aligned fields: + +image::display_decode_value_dv2.svg[] + +Here the `sync` pattern starts at `0xb79` (bit `0`) and ends at `0xb7a.3` (exclusive) and has a size of `1` byte and `3` bits, `11` bits in total (`8+3`). + +There are also some other `display` aliases: + +- `da` is `display({array_truncate: 0, string_truncate: 0})` don't truncate array and strings. +- `dd` is `display({array_truncate: 0, string_truncate: 0, display_bytes: 0})` don't truncate array and strings, show all raw bytes. +- `dv` is `display({array_truncate: 0, string_truncate: 0, verbose: true})` don't truncate array and strings and display verbosely. +- `ddv` is `display({array_truncate: 0, string_truncate: 0, display_bytes: 0, verbose: true})` don't truncate array and strings, show all raw bytes and display verbosely. + +== Formats + +By default fq will try to automatically determine input format. In some cases this might fail or is not +possible, then a format can be specified using `-d NAME`. It's possible sometimes to force decode and get +a partial or broken result using `-o force=true`. + +[source,console] +---- +# decode as msgpack +$ fq -d msgpack d file +# force decode as msgpack +$ fq -d msgpack -o force=true d file +# see msgpack format help +$ fq -h msgpack +# list supported formats +$ fq -h formats +---- + +=== Format options + +Some formats has own options that can be set using `-o`. For +example the `mp4` format has a `decode_samples` option that controls +if individual samples should be decoded. To disable it one can do +`fq -o decode_samples=false . file.mp4`. See format list for options. + +=== Format functions + +In addition to using `-d` all format decoders are also available as normal jq functions. +Each format provides multiple functions: + +``:: + Decode and return a decode value even on error. Ex: `... | mp4` + +`($options)`:: + Same as above with format options. Ex: `... | mp4({decode_samples: false})` + +`from_`:: + Decode or throw on error. Ex: `... | from_mp4` + +`from_($options)`:: + Same as above with format options Ex: `... | from_mp4({decode_samples: false})` + +Example usage: + +[source,console] +---- +# decode jpeg found inside some other format +$ fq '.some[].query | jpeg' file + +# decode jpeg at byte range 100-200 +$ fq -d bytes '.[100:200] | jpeg' file +---- + +=== Supported formats + +////jq-eval +( formats +| to_entries +| sort_by(.key) +| .[] +| . as {key: $key, value: $f} +| [ ( _registry.files[][] | select(.name=="\($key).md").data + | markdown + | _markdown_to_asciidoc + | . + ) + , if .value.decode_in_arg then + ( "" + , ".Options" + , ( $f.decode_in_arg + | to_entries[] + | "`*-o \(.key)=\(.value | tojson)*`:::" + , " \($f.decode_in_arg_doc[.key] | if . == "" then "No description" end)" + , "" + ) + ) + else empty + end + ] as $c +| "`*\($key)*`::" +, " \(.value.description)" +, if $c != [] then + ( "+" + , "--" + , $c[] + , "--" + , "+" + ) + else empty + end +, "" +) +////jq-eval + +== Encodings, serializations and hashes + +In addition to binary formats fq also supports various encodings and serialization formats. + +At the moment fq does not have any dedicated argument for serialization formats but raw string input `-R` slurp `-s` and raw string output `-r` can make things easier. The combination `-Rs` will read all inputs into one string (same as jq). + +Note that `from*` functions output jq values and `to*` functions take jq values as input so in some cases not all information will be properly preserved. For example, the element and attribute order might change and text and comment nodes might move or be merged. https://github.com/mikefarah/yq[yq] might be a better tool if that is needed. + +Some example usages: + +[source,console] +---- +# read yml (format is probed, use -d yaml to force) and do some query +$ fq '...' file.yml + +# convert YAML to JSON +# note the -r for raw string output, without it a JSON string with escaped JSON would be output +$ fq -r 'tojson({indent:2})' file.yml + +# add token to URL +$ echo -n "https://host.org" | fq -Rsr 'from_url | .user.username="token" | to_url' +https://token@host.org + +# top 3 hosts in src or href attributes: +# -d to decode as html, can't be probed as html5 parsers always produce some parse tree +# [...] to start collect values into an array +# .. | ."@src"?, ."@href"? | values, recurse and try (?) to get src and href attributes and filter out nulls +# from_url.host | values, parse as url and filter out those without a host +# count to count unique values, returns [[key, count], ...] +# reverse sort by count and pick first 3 +# map [key, count] tuples into {key: key, value: count} +# from_entries, convert into object +$ curl -s https://www.discogs.com/ | fq -d html '[.. | ."@src"?, ."@href"? | values | from_url.host | values] | count | sort_by(-.[1])[0:3] | map({key: .[0], value: .[1]}) | from_entries' +{ + "blog.discogs.com": 9, + "st.discogs.com": 10, + "www.discogs.com": 14 +} + +# shows how serialization functions can be used on any string, how to transform values and output some other format +# read and decode zip file and start an interactive REPL +$ fq -i . <(curl -sL https://github.com/stefangabos/world_countries/archive/master.zip) +# select from interesting xml file +zip> .local_files[] | select(.file_name == "world_countries-master/data/countries/en/world.xml").uncompressed | repl +# convert xml into jq value +> .local_files[95].uncompressed string> from_xml | repl +# sort countries by and select the first one +>> object> .countries.country | sort_by(."@name") | first | repl +# see what current input is +>>> object> . +{ + "@alpha2": "af", + "@alpha3": "afg", + "@id": "4", + "@name": "Afghanistan" +} +# remove "@" prefix from keys and convert to YAML and print it +>>> object> with_entries(.key |= .[1:]) | to_yaml | print +alpha2: af +alpha3: afg +id: "4" +name: Afghanistan +# exit all REPLs back to shell +>>> object> ^D +>> object> ^D +> .local_files[95].uncompressed string> ^D +zip> ^D +---- + +=== XML and HTML + +* `from_xml`/`from_xml($opts)` Parse XML into jq value. `$opts` are: +** `{seq: true}` preserve element ordering if more than one sibling. + +** `{array: true}` use nested `[name, attributes, children]` arrays to represent elements. Attributes will be `null` if none and children will be `[]` if none, this is to make it easier to work with as the array always has 3 values. `to_xml` does not require this. + +* `from_html`/`from_html($opts)` Parse HTML into jq value. + + Similar to `from_xml` but parses html5 in non-script mode. Will always have a `html` root with `head` and `body` elements. + + + `$opts` are: +** `{array: true}` use nested arrays to represent elements. + +** `{seq: true}` preserve element ordering if more than one sibling. + +* `to_xml`/`to_xml($opts)` Serialize jq value into XML. + + Assumes object representation if input is an object, and nested arrays if input is an array. + + Will automatically add a root `doc` element if jq value has more than one root element. + + If a `#seq` is found on at least one element all siblings will be sorted by sequence number. Attributes are always sorted. + + + `$opts` are: +** `{indent: number}` indent child elements. + +XML elements can be represented as jq value in two ways, as objects (inspired by https://github.com/clbanning/mxj[mxj] and https://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html[xml.com's Converting Between XML and JSON]) or nested arrays. Both representations are lossy and might lose ordering of elements, text nodes and comments. In object representation `from_xml`, `from_html` and `to_xml` support `{seq: true}` option to parse/serialize `{"#seq": }` attributes to preserve element sibling ordering. + +The object version is denser and convenient to query, the nested arrays version is probably easier to use when generating XML. + +Let's assume `$xml` is this XML document as a string: +[source,xml] +---- + + + text + text + +---- + +With object representation an element is represented as: + +** Attributes as `@` prefixed `@` keys. +** Text nodes as `#text`. +** Comment nodes as `#comment` keys. +** For explicit sibling ordering `#seq` keys with a number, can be negative, assumed zero if missing. +** Child element with only text as `` key with text as value. +** Child element with more than just text as `` key with value an object. +** Multiple child element siblings with same name as `` key with value as array with strings and objects. + +[source,console] +---- +> $xml | from_xml +{ + "doc": { + "child": [ + { + "@attr": "1" + }, + { + "#text": "text", + "@attr": "2" + } + ], + "other": "text" + } +} +---- + +With nested array representation, an array with these values `["", {attributes...}, [children...]]`. + +- Index 0 is an element name. +- Index 1 object attributes (including `#text` and `#comment` keys). +- Index 2 array of child elements. + +[source,console] +---- +> $xml | from_xml({array: true}) +[ + "doc", + null, + [ + [ + "child", + { + "attr": "1" + }, + [] + ], + [ + "child", + { + "#text": "text", + "attr": "2" + }, + [] + ], + [ + "other", + { + "#text": "text" + }, + [] + ] + ] +] +---- + +Parse and include `#seq` attributes if needed: + +[source,console] +---- +> $xml | from_xml({seq:true}) +{ + "doc": { + "child": [ + { + "#seq": 0, + "@attr": "1" + }, + { + "#seq": 1, + "#text": "text", + "@attr": "2" + } + ], + "other": { + "#seq": 2, + "#text": "text" + } + } +} +---- + +Select values in ``, remove ``, add a `` element, serialize to xml with 2 space indent and print the string + +[source,console] +---- +> $xml | from_xml.doc | del(.child) | .new = "abc" | {root: .} | to_xml({indent: 2}) | println + + abc + text + +---- + +=== JSON + +* `fromjson` Parse JSON into jq value. +* `tojson`/`tojson($opts)` Serialize jq value into JSON. `$opts` are: +** `{indent: number}` Indent depth. +* `from_jsonl` Parse JSON lines into jq array. +* `to_jsonl` Serialize jq array into JSONL. + +=== jq-flavoured JSON + +* `from_jq` Parse jq-flavoured JSON into jq value. +* `to_jq`/`to_jq($opts)` Serialize jq value into jq-flavoured JSON. jq-flavoured JSON has optional key quotes, `#` comments and can have trailing comma in objects. `$opts` are: +** `{indent: number}` Indent depth. + +Note that `fromjson` and `tojson` use different naming conventions as they originate from jq's standard library. + +=== YAML + +* `from_yaml` Parse YAML into jq value. +* `to_yaml`/`to_yaml($opts)` Serialize jq value into YAML. `$opts` are: +** `{indent: number}` Indent depth. + +=== TOML + +* `from_toml` Parse TOML into jq value. +* `to_toml`/`to_toml($opts)` Serialize jq value into TOML. `$opts` are: +** `{indent: number}` Indent depth. + +=== CSV + +* `from_csv`/`from_csv($opts)` Parse CSV into jq value. + + To work with tab separated values you can use `from_csv({comma: "\t"})` or `fq -d csv -o 'comma="\t"'`. + + `$opts` are: +** `{comma: string}` field separator, default ",". + +** `{comment: string}` comment line character, default "#". + +* `to_csv`/`to_csv($opts)` Serialize jq value into CSV. `$opts` are: +** `{comma: string}` field separator, default ",". + + +=== XML entities + +- `from_xmlentities` Decode XML entities. +- `to_xmlentities` Encode XML entities. + +=== URL + +- `from_urlpath` Decode URL path component. +- `to_urlpath` Encode URL path component. Whitespace as %20. +- `from_urlencode` Decode URL query encoding. +- `to_urlencode` Encode URL to query encoding. Whitespace as "+". +- `from_urlquery` Decode URL query into object. For duplicate keys value will be an array. +- `to_urlquery` Encode object into query string. +- `from_url` Decode URL into object. ++ +-- +[source,console] +---- +> "schema://user:pass@host/path?key=value#fragment" | from_url +{ + "fragment": "fragment", + "host": "host", + "path": "/path", + "query": { + "key": "value" + }, + "rawquery": "key=value", + "scheme": "schema", + "user": { + "password": "pass", + "username": "user" + } +} +---- +-- +- `to_url` Encode object into URL string. + +=== Hex and base64 + +- `from_hex` Decode hex string to binary. +- `to_hex` Encode binary into hex string. +- `from_base64`/`from_base64($opts)` Decode base64 encodings into binary. `$opts` are: + - `{encoding:string}` encoding variant: `std` (default), `url`, `rawstd` or `rawurl` +- `to_base64`/`to_base64($opts)` Encode binary into base64 encodings. `$opts` are: + - `{encoding:string}` encoding variant: `std` (default), `url`, `rawstd` or `rawurl` + +=== Hash functions + +- `to_md4` Hash binary using md4. +- `to_md5` Hash binary using md5. +- `to_sha1` Hash binary using sha1. +- `to_sha256` Hash binary using sha256. +- `to_sha512` Hash binary using sha512. +- `to_sha3_224` Hash binary using sha3 224. +- `to_sha3_256` Hash binary using sha3 256. +- `to_sha3_384` Hash binary using sha3 384. +- `to_sha3_512` Hash binary using sha3 512. + +=== Text encodings + +- `to_iso8859_1` Encode string as ISO8859-1 into binary. +- `from_iso8859_1` Decode binary as ISO8859-1 into string. +- `to_utf8` Encode string as UTF8 into binary. +- `from_utf8` Decode binary as UTF8 into string. +- `to_utf16` Encode string as UTF16 into binary. +- `from_utf16` Decode binary as UTF16 into string. +- `to_utf16le` Encode string as UTF16 little-endian into binary. +- `from_utf16le` Decode binary as UTF16 little-endian into string. +- `to_utf16be` Encode string as UTF16 big-endian into binary. +- `from_utf16be` Decode binary as UTF16 big-endian into string. + +== Interactive REPL + +The REPL can be useful in some scenarios: + +- When decoding is slow you can reuse the decode result. +- Dig thru a file using sub-REPL to cut down on typing. +- Use auto-completion to speed up typing. + +[source,console] +---- +# start REPL with no (null) input +$ fq -i +null> +# same as +$ fq -ni +null> + +# in the REPL you will see a prompt indicating current input and you can type a jq expression to evaluate. + +# start REPL with one file as input +$ fq -i . doc/file.mp3 +mp3> +# basic arithmetic and jq expressions +mp3> 1+1 +2 +mp3> 1, 2, 3 | . * 2 +2 +4 +6 +mp3> [1, 2, 3] | add +6 +# "." is the identity function which just returns current input, the mp3 file. +mp3> . +# access the first frame in the mp3 file +mp3> .frames[0] +# start a new nested REPL with first frame as input +mp3> .frames[0] | repl +# prompt shows "path" to current input and that it's an mp3_frame. +# Ctrl-D to exit REPL or to shell if last REPL +> .frames[0] mp3_frame> ^D +# "jq" value of layer in first frame +mp3> .frames[0].header.layer | tovalue +3 +mp3> .frames[0].header.layer * 2 +6 +# symbolic value, same as "jq" value +mp3> .frames[0].header.layer | tosym +3 +# actual underlying decoded value +mp3> .frames[0].header.layer | toactual +1 +# description of value +mp3> .frames[0].header.layer | todescription +"MPEG Layer 3" +mp3> ^D +$ +---- + +Use Ctrl-D to exit and Ctrl-C to interrupt current evaluation. + +== Examples + +=== Basic usage + +fq tries to behave the same way as jq as much as possible, so you can do: + +[source,console] +---- +fq . file +fq < file +cat file | fq +fq . < file +fq . *.png *.mp3 +fq '.frames[0]' *.mp3 +fq '.frames[-1] | tobytes' file.mp3 > last_frame +---- + +=== Common usages + +[source,console] +---- +# recursively display decode tree but truncate long arrays +fq d file +# same as +fq display file + +# display all bytes for each value +fq dd file +# same as +fq 'd({array_truncate: 0, string_truncate: 0, display_bytes: 0})' file + +# display 200 bytes for each value +fq 'd({display_bytes: 200})' file + +# recursively display decode tree without truncating +fq da file +# same as +fq 'd({array_truncate: 0, string_truncate: 0})' file + +# display a specific decode tree one level +fq '.path[1].to.value' file +# display a specific decode tree all levels +fq '.path[1].to.value | d' file +fq '.path[1].to.value | dd' file +fq '.path[1].to.value | da' file + +# recursively and verbosely display decode tree +fq dv file +# same as +fq 'd({array_truncate: 0, string_truncate: 0, verbose: true})' file + +# JSON representation for whole file +fq tovalue file +# or use -V (--value-output) that does tovalue automatically +fq -V . file +# or -Vr if the value is a string and you want a "raw" string +fq -Vr .path.to.string file +# JSON but raw bit fields truncated +fq -o bits_format=truncate tovalue file +# JSON but raw bit fields as md5 hex string +fq -o bits_format=md5 tovalue file +# JSON but raw bit fields as byte arrays +fq -o bits_format=byte_array tovalue file +# look up a path +fq '.some[1].path' file +# look up a path and output JSON +fq -V '.some[1].path' file +# can be a query that outputs multiple values +# this outputs first and last value in .some array and .path, three values in total +fq -V '.some[0,-1], .path' file + +# grep whole tree by value +fq 'grep("^prefix")' file +fq 'grep(123)' file +# grep whole tree by condition +fq 'grep_by(. >= 100 and . <= 100)' file + +# recursively look for values fulfilling some condition +fq '.. | select(.type=="trak")?' file +fq 'grep_by(.type=="trak")' file +# grep_by(f) is an alias for .. | select(f)?, that is: recurse, select and ignore errors + +# recursively look for decode value roots for a format +fq '.. | select(format=="jpeg")' file +# can also use grep_by +fq 'grep_by(format=="jpeg")' file + +# recursively look for first decode value root for a format +fq 'first(.. | select(format=="jpeg"))' file +fq 'first(grep_by(format=="jpeg"))' file + +# decode file as mp4 and return a result even if there are some errors +fq -d mp4 file.mp4 +# decode file as mp4 and also ignore validity assertions +fq -o force=true -d mp4 file.mp4 +---- diff --git a/doc/fq.1.tmpl.adoc.jq b/doc/fq.1.tmpl.adoc.jq new file mode 100644 index 000000000..42a282d6c --- /dev/null +++ b/doc/fq.1.tmpl.adoc.jq @@ -0,0 +1,25 @@ +def fq_tmpl_adoc($replace_svg): + # ```jq-eval + # ... + # ``` + gsub( + "////jq-eval\n(?[\\s\\S]*?)\n////jq-eval"; + [_eval(.expr; {})] | join("\n") + ) | + # image::display_decode_value_d.svg[] + if $replace_svg then + gsub( + "image::(?.*?)\\[\\]"; + ( .image + | gsub(".svg"; ".txt") + | open + | tobytes + | tostring + | "[source,shell]\n----\n\(.)----\n" + ) + ) + end | + gsub( + "\\$FQ_VERSION"; + _main_input.version + ); diff --git a/doc/usage.md b/doc/usage.md deleted file mode 100644 index e48dc4a4e..000000000 --- a/doc/usage.md +++ /dev/null @@ -1,1059 +0,0 @@ -## Basic usage - -fq tries to behave the same way as jq as much as possible, so you can do: - -```sh -fq . file -fq < file -cat file | fq -fq . < file -fq . *.png *.mp3 -fq '.frames[0]' *.mp3 -fq '.frames[-1] | tobytes' file.mp3 > last_frame -``` - -### Common usages - -```sh -# recursively display decode tree but truncate long arrays -fq d file -# same as -fq display file - -# display all bytes for each value -fq dd file -# same as -fq 'd({display_bytes: 0})' file - -# display 200 bytes for each value -fq 'd({display_bytes: 200})' file - -# recursively display decode tree without truncating -fq da file -# same as -fq 'd({array_truncate: 0})' file - -# display a specific decode tree one level -fq '.path[1].to.value' file -# display a specific decode tree all levels -fq '.path[1].to.value | d' file -fq '.path[1].to.value | dd' file -fq '.path[1].to.value | da' file - -# recursively and verbosely display decode tree -fq dv file -# same as -fq 'd({verbose: true})' file - -# JSON representation for whole file -fq tovalue file -# or use -V (--value-output) that does tovalue automatically -fq -V . file -# or -Vr if the value is a string and you want a "raw" string -fq -Vr .path.to.string file -# JSON but raw bit fields truncated -fq -o bits_format=truncate tovalue file -# JSON but raw bit fields as md5 hex string -fq -o bits_format=md5 tovalue file -# JSON but raw bit fields as byte arrays -fq -o bits_format=byte_array tovalue file -# look up a path -fq '.some[1].path' file -# look up a path and output JSON -fq -V '.some[1].path' file -# can be a query that outputs multiple values -# this outputs first and last value in .same array and .path, three values in total -fq -V '.some[0,-1], .path' file - -# grep whole tree by value -fq 'grep("^prefix")' file -fq 'grep(123)' file -# grep whole tree by condition -fq 'grep_by(. >= 100 and . =< 100)' file - -# recursively look for values fulfilling some condition -fq '.. | select(.type=="trak")?' file -fq 'grep_by(.type=="trak")' file -# grep_by(f) is alias for .. | select(f)?, that is: recurse, select and ignore errors - -# recursively look for decode value roots for a format -fq '.. | select(format=="jpeg")' file -# can also use grep_by -fq 'grep_by(format=="jpeg")' file - -# recursively look for first decode value root for a format -fq 'first(.. | select(format=="jpeg"))' file -fq 'first(grep_by(format=="jpeg"))' file - -# decode file as mp4 and return a result even if there are some errors -fq -d mp4 file.mp4 -# decode file as mp4 and also ignore validity assertions -fq -o force=true -d mp4 file.mp4 -``` - -### CLI arguments - -Most of jq's CLI arguments work with fq. But here are some additional ones specific to fq: - -#### Decode format `--decode`, `-d NAME` - -Force format to decode instead of probing. - -`NAME` is a name of a format, ex `-d mp4`, see `-h formats` for list of formats. - -#### Interactive REPL `--repl`,`-i` - -Start interactive REPL. - -Can be used with no input, one and multiple inputs, for example just `fq -i ` starts a REPL with `null` input, `fq -i 123` with the number 123 as input, `fq -i . a b` with two files as input. This also works with `--slurp`. In the REPL it is also possible to start a sub-REPLs by ending a query with ` | repl`, use ctrl-D to exit the sub-REPL. The sub-REPL will evaluate separately on each output from the query it was started. Use `[] | repl` if you want to "slurp" into an array. - -#### Set option `--options`,`-o KEY=VALUE|@PATH` - -`KEY` is name of option - -`VALUE` will be interpreted as a JSON value if possible otherwise a string, ex -o `name=abc` and `-o name='"abc"'` is the same. - -`@PATH` will read string from file at `PATH`. - -Specify a global option or a format option, ex: `-o decode_samples=false` would for some container decoders like `mp4` and `matroska` disable decoding of samples. - -#### Value output `--value-output`, `-V` - -Output JSON value instead of decode tree. Use `-Vr` if you want raw string (no quotes). - -### Display output - -`display` or `d` is the main function for displaying values and is also the function that will be used if no other output function is explicitly used. If its input is a decode value it will output a dump and tree structure or otherwise it will output as JSON. - -Below demonstrates some usages: - -First and second example does the same thing, inputs `"hello"` to `display`. - -![fq demo](display_json.svg) - -In the next few examples we select out the first "edit list" box in an mp4 file, it's a list of which part of the media track to be included during playback, and displays it in various ways. - -Default if not explicitly used `display` will only show the root level: - -![fq demo](display_decode_value.svg) - -First row shows a ruler with byte offset into the line and jq path for the value. - -The columns are: -- Start address for the line. For example we see that `type` starts at `0xd60` (row) + `0x09` (column). -- Hex representation of input bits for value. Will show the whole byte even if the value only partially uses bits from it. -- ASCII representation of input bits for value. Will show the whole byte even if the value only partially uses bits from it. -- Tree structure of decoded value, symbolic value and description. - -Notation: -- `{}` value is an object that might have nested values. -- `[start:end]` value is an array with index starting at `start` and ending at `end` (exclusive). - - -With `display` or `d` it will recursively show the whole tree: - -![fq demo](display_decode_value_d.svg) - -Same but verbose `dv`: - -![fq demo](display_decode_value_dv.svg) - -In verbose mode bit ranges and array element names as shown. - -Bit ranges uses `[.]-[.]` as notation where `.` is left out if byte aligned. For example `type` starts at byte `0xd69` bit `0` (`.0` is left out) and ends at `0xd6d` bit `0` (exclusive) and have a size of `4` bytes. - -This verbosely displays the header of the second frame in an mp3 file which has a bunch of non-byte-aligned fields: - -![fq demo](display_decode_value_dv2.svg) - -Here the `sync` pattern starts at `0xb79` (bit `0`) and ends at `0xb7a.3` (exclusive) and has a size of `1` byte and `3` bits, `11` bits in total (`8+3`). - -There are also some other `display` aliases: -- `da` is `display({array_truncate: 0})` don't truncate arrays. -- `dd` is `display({array_truncate: 0, string_truncate: 0, display_bytes: 0})` don't truncate arrays and raw bytes. -- `dv` is `display({array_truncate: 0, string_truncate: 0, verbose: true})` don't truncate arrays and display verbosely. -- `ddv` is `display({array_truncate: 0, string_truncate: 0, display_bytes: 0 verbose: true})` don't truncate arrays and raw bytes. and display verbosely. - -## Interactive REPL - -The interactive [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) -has auto completion and nested REPL support: - -``` -# start REPL with null input -$ fq -i -null> -# same as -$ fq -ni -null> - -# in the REPL you will see a prompt indicating current input and you can type jq expression to evaluate. - -# start REPL with one file as input -$ fq -i . doc/file.mp3 -mp3> - -$ fq -i . doc/file.mp3 -# basic arithmetic and jq expressions -mp3> 1+1 -2 -mp3> 1, 2, 3 | . * 2 -2 -4 -6 -mp3> [1, 2, 3] | add -6 -# "." is the identity function which just returns current input, the mp3 file. -mp3> . -# access the first frame in the mp3 file -mp3> .frames[0] -# start a new nested REPL with first frame as input -mp3> .frames[0] | repl -# prompt shows "path" to current input and that it's an mp3_frame. -# Ctrl-D to exit REPL or to shell if last REPL -> .frames[0] mp3_frame> ^D -# "jq" value of layer in first frame -mp3> .frames[0].header.layer | tovalue -3 -mp3> .frames[0].header.layer * 2 -6 -# symbolic value, same as "jq" value -mp3> .frames[0].header.layer | tosym -3 -# actual underlying decoded value -mp3> .frames[0].header.layer | toactual -1 -# description of value -mp3> .frames[0].header.layer | todescription -"MPEG Layer 3" -mp3> ^D -$ -``` - -Use Ctrl-D to exit and Ctrl-C to interrupt current evaluation. - -## Example usages - -#### Second mp3 frame header as JSON - -```sh -fq '.frames[1].header | tovalue' file.mp3 -``` - -#### Byte start position for the first 10 mp3 frames in an array - -```sh -fq '.frames[0:10] | map(tobytesrange.start)' file.mp3 -``` - -#### Decode at range - -```sh -# decode byte range 100 to end as mp3_frame -fq -d bytes '.[100:] | mp3_frame | d' file.mp3 -# decode byte range 10 bytes from .somefield and preserve relative position in file -fq '.somefield | tobytesrange[10:] | mp3_frame | d' file.mp3 -``` - -#### Show AVC SPS difference between two mp4 files - -`-n` tells fq to not have an implicit `input`, `f` is a function to select out some interesting value, call `diff` with two arguments, -decoded value for `a.mp4` and `b.mp4` filtered thru `f`. - -```sh -fq -n 'def f: .. | select(format=="avc_sps"); diff(input|f; input|f)' a.mp4 b.mp4 -``` - -#### Extract first JPEG found in file - -Recursively look for the first value that is a `jpeg` decode value root. Use `tobytes` to get bytes for value. Redirect bytes to a file. - -```sh -fq 'first(.. | select(format=="jpeg")) | tobytes' file > file.jpeg -``` - -#### Sample size histogram - -Recursively look for a all sample size boxes "stsz" and use `?` to ignore errors when doing `.type` on arrays etc. Save reference to box, count unique values, save the max, output the path to the box and output a histogram scaled to 0-100. - -```sh -fq '.. | select(.type=="stsz")? as $stsz | .entries | count | max_by(.[1])[1] as $m | ($stsz | topath | path_to_expr), (.[] | "\(.[0]): \((100*.[1]/$m)*"=") \(.[1])") | println' file.mp4 -``` - -#### Find TCP streams that looks like HTTP GET requests in a PCAP file - -Use `grep` to recursively find strings matching a regexp. - -```sh -fq '.tcp_connections | grep("GET /.* HTTP/1.?")' file.pcap -``` - -#### Use representation of a format - -Some formats like `msgpack`, `bson` etc are used to represent some data structure. In those cases the `torepr` -function can be used to get the representation. - -```sh -# whole represented value -fq -d msgpack torepr file.msgpack -# value of the key "field" from the represented value -fq -d msgpack `torepr.field` file.msgpack -# query or transform represented value -fq -d msgpack 'torepr | ...' file.msgpack -``` - -#### Widest PNG in a directory -```sh -$ fq -rn '[inputs | [input_filename, first(.chunks[] | select(.type=="IHDR") | .width)]] | max_by(.[1]) | .[0]' *.png -``` - -#### What values include the byte at position 0x123 -```sh -$ fq '.. | select(scalars and in_bytes_range(0x123))' file -``` - -## Support formats - -See [formats](formats.md) - -## The jq language - -fq is based on the [jq language](https://stedolan.github.io/jq/) and for basic usage its syntax -is similar to how object and array access looks in JavaScript or JSON path, `.food[10]` etc. but -it can do much more and is a very expressive language. - -To get the most out of fq it's recommended to learn more about jq, here are some good starting points: - -- [jq manual](https://stedolan.github.io/jq/manual/) -- [Peter Koppstein's A Stream oriented Introduction to jq](https://github.com/pkoppstein/jq/wiki/A-Stream-oriented-Introduction-to-jq) -- [jq wiki: Language Description](https://github.com/stedolan/jq/wiki/jq-Language-Description) -- [jq wiki: page Cookbook](https://github.com/stedolan/jq/wiki/Cookbook) -- [jq wiki: Pitfalls](https://github.com/stedolan/jq/wiki/How-to:-Avoid-Pitfalls) -- [FAQ](https://github.com/stedolan/jq/wiki/FAQ) - -For a more convenient jq experience these might be interesting: - -- [jq Dash docset](https://github.com/wader/jq-dash-docset) -- [vscode-jq](https://github.com/wader/vscode-jq) -- [jq-lsp](https://github.com/wader/jq-lsp) - -Common beginner gotchas are: -- jq's use of `;` and `,`. jq uses `;` as argument separator -and `,` as output separator. To call a function `f` with two arguments use `f(1; 2)`. If you do `f(1, 2)` you pass a -single argument `1, 2` (a lambda expression that outputs `1` and then outputs `2`) to `f`. -- Expressions can return or "output" zero or more values. This is how loops, foreach etc is -achieved. -- Expressions have one implicit input and output value. This how pipelines like `1 | . * 2` work. - -## Differences to jq - -- All [gojq's differences to jq](https://github.com/itchyny/gojq#difference-to-jq). -Notably it adds support for arbitrary-precision integers. -- Supports raw strings using back-ticks. \`\\(1234)\u1234\` results in the string `"\\(1234)\\u1234"`. -- Supports hexadecimal `0xab`, octal `0o77` and binary `0b101` integer literals. Also support grouping using underscore `0xaa_bb_cc_dd`. -- Try include using a ending question mark `include "file?";` that doesn't fail if file is missing or has errors. -- Some values can act as an object with keys even when it's an array, number etc. -- There can be keys hidden from `keys` and `[]`. -- Some values are readonly and can't be updated or will convert to JSON on update. -- Mixing `--args` and `--jsonargs` does not behave the same. - -### Types specific to fq - -fq has two additional types compared to jq, decode value and binary. In standard jq expressions they will in most cases behave as some standard jq type. - -### Decode value - -This type is returned by decoders and it is used to represent parts of the decoded input. It can act as all standard jq types, object, array, number, string etc. - -Each decode value has these properties: -- A bit range in the input - - Can be accessed as a binary using `tobits`/`tobytes`. Use the `start` and `size` keys to access position and size. - - `.name` as bytes `.name | tobytes` - - Bit 4-8 of `.name` as bits `.name | tobits[4:8]` - -Each non-compound decode value has these properties: -- An actual value: - - This is the decoded representation of the bits, a number, string, bool etc. - - Can be accessed using `toactual`. -- An optional symbolic value: - - Is usually a mapping of the actual to symbolic value, ex: map number to a string value. - - Can be accessed using `tosym`. -- An optional description: - - Can be accessed using `todescription` -- `parent` is the parent decode value -- `parents` is the all parent decode values -- `topath` is the jq path for the decode value -- `torepr` convert decode value to its representation if possible - -The value of a decode value is the symbolic value if available and otherwise the actual value. To explicitly access the value use `tovalue`. In most expressions this is not needed as it will be done automatically. - -### Binary - -Binaries are raw bits with a unit size, 1 (bits) or 8 (bytes), that can have a non-byte aligned size. Will act as byte padded strings in standard jq expressions. - -Use `tobits` and `tobytes` to create them from decode values, strings, numbers or binary arrays. `tobytes` will, if needed zero pad most significant bits to be byte aligned. - -There is also `tobitsrange` and `tobytesrange` which does the same thing but will preserve its source range when displayed. - -- `"string" | tobytes` produces a binary with UTF8 codepoint bytes. -- `1234 | tobits` produces a binary with the unsigned big-endian integer 1234 with enough bits to represent the number. Use `tobytes` to get the same but with enough bytes to represent the number. This is different to how numbers work inside binary arrays where they are limited to 0-255. -- `["abc", 123, ...] | tobytes` produce a binary from a binary array. See [binary array](#binary-array) below. -- `.[index]` access bit or byte at index `index`. Index is in units. - - `[0x12, 0x34, 0x56] | tobytes[1]` is `0x35` - - `[0x12, 0x34, 0x56] | tobits[3]` is `1` -- `.[start:]`, `.[start:end]` or `.[:end]` is normal jq slice syntax and will slice the binary from `start` to `end`. `start` and `end` is in units. - - `[0x12, 0x34, 0x56] | tobytes[1:2]` will be a binary with the byte `0x34` - - `[0x12, 0x34, 0x56] | tobits[4:12]` will be a binary with the byte `0x23` - - `[0x12, 0x34, 0x56] | tobits[4:20]` will be a binary with the byte `0x23`, `0x45` - - `[0x12, 0x34, 0x56] | tobits[4:20] | tobytes[1:]` will be a binary with the byte `0x45`, - - Both `.[index]` and `.[start:end]` support negative indices to index from end. -- `explode` output an array with all byte or bits as integers. - -#### Binary array - -Is an array of numbers, strings, binaries or other nested binary arrays. When used as input to `tobits`/`tobytes` the following rules are used: -- Number is a byte with value be 0-255 -- String it's UTF8 codepoint bytes -- Binary as is -- Binary array used recursively - -Binary arrays are similar to and inspired by [Erlang iolist](https://www.erlang.org/doc/man/erlang.html#type-iolist). - -Some examples: - -`[0, 123, 255] | tobytes` will be binary with 3 bytes 0, 123 and 255 - -`[0, [123, 255]] | tobytes` same as above - -`[0, 1, 1, 0, 0, 1, 1, 0 | tobits]` will be binary with 1 byte, 0x66 an "f" - -`[(.a | tobytes[-10:]), 255, (.b | tobits[:10])] | tobytes` the concatenation of the last 10 bytes of `.a`, a byte with value 255 and the first 10 bits of `.b`. - -## Functions - -fq has all the same standard library jq functions and in addition some new ones. - -### Additional generic functions - -#### `grep_by(f)` -Recursively select using a filter. Ex: `grep_by(. > 180 and . < 200)`, `first(grep_by(format == "id3v2"))`. - -#### `group` -Group values, same as `group_by(.)`. - -#### `streaks`, `streaks_by(f)` -Like `group` but groups streaks based on condition. - -#### `count`, `count_by(f)` -Like `group` but counts groups lengths based on condition. - -#### `delta`, `delta_by(f)` -Array with difference between consecutive. `delta` is same as `delta_by(.b - .a)`. - -#### `chunk($size)` -Split array or string into `$size` length chunks. Last chunk might be shorter. - -#### `path_to_expr` -Converts a path value `["key", 1]` to a string `".key[1]"`. - -#### `expr_to_path` -Converts from a string `".key[1]"` to path value `["key", 1]`. - -#### `diff($a; $b)` -Produce a diff between `$a` and `$b`. Differences are represented as a object `{a: , b: }`. - -#### `band`, `bor`, `bxor`, `bsl`, `bsr`, `bnot`. -Bitwise functions. Works the same as jq math functions. Functions with no arguments like `1 | bnot` uses only input, functions with more than one argument ignores input, `bsl(1; 3)`. - -#### `repl`/`repl($opts)` -Nested REPL. Must be last in a pipeline. `1 | repl`, can "slurp" outputs. Ex: `1, 2, 3 | repl`, `[1,2,3] | repl({compact: true})`. - -#### `slurp("")` -Slurp outputs and save them to `$name`. Must be last in the pipeline. Will be available as a global array `$name`. Ex `1,2,3 | slurp("a")`, `$a[]` same as `spew("a")`. - -#### `spew`/`spew("")` -Output previously slurped values. - -#### `spew` -Outputs all slurps as an object. `spew("")` outputs one slurp. Ex: `spew("a")`. - -#### `paste` -Read string from stdin until ^D. Useful for pasting text. Ex: `paste | from_pem | asn1_ber | repl` read from stdin then decode and start a new sub-REPL with result. - -### Format decode functions - -Format decode functions are available in two forms, just `mp3` or `mp3($opts)` that returns a decode value even on error and `from_mp3` or `from_mp3($opts)` which throws error on decode error. - -The only general format option currently is `force` to ignore decoder asserts. -For example to decode as mp3 and ignore assets do `mp3({force: true})` or `decode("mp3"; {force: true})`. From command line you can either do `fq -d mp3 -o force=true . file.mp3` or `fq -d bytes 'mp3({force: true})' file.mp3`. - -Some formats have own options that can be specificed as part of `$opts` or as `-o name=value`. To see options for a format do `fq -h mp3` or `help(mp3)` in a REPL. From command line you can either do `fq -d mp3 -o max_sync_seek=100 . file.mp3` or `fq -d bytes 'mp3({max_sync_seek: 100})' file.mp3`. - -#### `decode`, `decode("")`, `decode(""; $opts)` -Decode format. - -#### `probe`, `probe($opts)` -Probe and decode format. - -#### ``, `($opts)` -Same as `decode("")` and `decode(""; $opts)`. Decode as format and return decode value even on decode error. - -#### `from_`, `from_($opts)` -Same as `decode("")` and `decode(""; $opts)` decode as format but throw error on decode error. - -Note that jq sometimes uses the notation `name/0`, `name/1` etc in error messages and documentation which means `/`. Same function names with different arity are treated as separate functions, but are usually related in some way in practice. - -#### `print`, `println`, `printerr`, `printerrln` -Print string or if not a string compact JSON value to stdout or stderr. - -#### `root` -Root decode value for decode value. - -#### `buffer_root` -Root decode value of buffer for decode value. - -#### `format_root` -Root decode value of format for decode value. - -#### `parent` -Parent decode value for decode value. - -#### `parents` -Outputs all parent decode values from decode value. - -#### `topath` -Path for decode value. Use `path_to_expr` to get a string representation. - -#### `tovalue`, `tovalue($opts)` -Symbolic, if available, or actual value for decode value. - -#### `toactual`, `toactual($opts)` -Actual value for decode value. - -#### `tosym`, `tosym($opts)` -Symbolic value for decode value. - -#### `todescription` -Description for decode value. - -#### `torepr` -Converts decode value into what it represents. For example converts msgpack decode value into a value representing its JSON representation. - -### Display functions - -Display shows hexdump, ASCII and tree column dump for decode values and jq value for other types. - -#### `d`/`d($opts)` -display value and truncate long arrays and binaries. - -#### `da`/`da($opts)` -Display value and don't truncate arrays. - -#### `dd`/`dd($opts)` -Display value and don't truncate arrays or binaries. - -#### `dv`/`dv($opts)` -Verbosely display value and don't truncate arrays but truncate binaries. - -#### `ddv`/`ddv($opts)` -Verbosely display value and don't truncate arrays or binaries. - -#### `hd`/`hexdump` -Hexdump value. - -### Binary values - -Binary values represents raw bits or bytes. When used in standard jq expressions they will behave as strings (UTF-8) with some exceptions listed below. - -- All regexp functions work with binary as input and pattern argument with these differences -compared to when using string input: - - All offset and length will be in bytes. - - For `capture` the `.string` value is a binary. - - If pattern is a binary it will be matched literally and not as a regexp. - - If pattern is a binary or flags include "b" each input byte will be read as separate code points -- `explode` is overloaded to work with binary. Will explode into array of the unit of the binary. -- `.[start:end]`, `.[:end]`, `.[start:]` - Slice binary from start to end preserve source range. - -#### `grep($v)`, `grep($v; $flags)`, `vgrep($v)`, `vgrep($v; $flags)`, `bgrep($v)`, `bgrep($v; $flags)` -Recursively match `$v`. - -`$v` is a scalar to match, where a string is treated as a regexp. A binary will match exact bytes. -`$flags` argument are regexp flags with additional flag "b" that will treat each byte in the input binary -as a code point. This makes it possible to match exact bytes. - -#### `fgrep($v)`, `fgrep($v; $flags)` -Recursively match field name in for decode value. - -#### `tobits` -Transform input to binary with bit as unit and don't preserve source range. - -#### `tobitsrange` -Transform input to binary with bit as unit and preserve source range. - -#### `tobytes` -Transform input to binary with byte as unit and don't preserve source range. - -#### `tobytesrange` -Transform input to binary with byte as unit and preserve source range. - -#### `open` -Open file for reading. - -### Naming inconsistencies - -jq's naming conversion is a bit inconsistent, some standard library functions are named `tojson` while others `from_entries`. fq follows this tradition a bit by but tries to use snake_case unless there is a good reason. - -Here are all the non-snake_case functions added by. Most of them deal with decode and binary values which are new "primitive" types: -- `toactual` -- `tobits` -- `tobitsrange` -- `tobytes` -- `tobytesrange` -- `todescription` -- `topath` -- `torepr` -- `tosym` -- `tovalue` - -### Encodings, serializations and hashes - -In addition to binary formats fq also support reading to and from encodings and serialization formats. - -At the moment fq does not have any dedicated argument for serialization formats but raw string input `-R` slurp `-s` and raw string output `-r` can make things easier. The combination `-Rs` will read all inputs into one string (same as jq). - -Note that `from*` functions output jq values and `to*` takes jq values as input so in some cases not all information will be properly preserved. For example, for the element and attribute order might change and text and comment nodes might move or be merged. [yq](https://github.com/mikefarah/yq) might be a better tool if that is needed. - -Some example usages: - -```sh -# read yml (format is probed, use -d yaml to force) and do some query -$ fq '...' file.yml - -# convert YAML to JSON -# note -r for raw string output, without a JSON string with JSON would outputted -$ fq -r 'tojson({indent:2})' file.yml - -# add token to URL -$ echo -n "https://host.org" | fq -Rsr 'from_url | .user.username="token" | to_url' -https://token@host.org - -# top 3 hosts in src or href attributes: -# -d to decode as html, can't be probed as html5 parsers always produce some parse tree -# [...] to start collect values into an array -# .. | ."@src"?, ."@href"? | values, recurse and try (?) to get src and href attributes and filter out nulls -# from_url.host | values, parse as url and filter out those without a host -# count to count unique values, returns [[key, count], ...] -# reverse sort by count and pick first 3 -# map [key, count] tuples into {key: key, values: count} -# from_entries, convert into object -$ curl -s https://www.discogs.com/ | fq -d html '[.. | ."@src"?, ."@href"? | values | from_url.host | values] | count | sort_by(-.[1])[0:3] | map({key: .[0], value: .[1]}) | from_entries' -{ - "blog.discogs.com": 9, - "st.discogs.com": 10, - "www.discogs.com": 14 -} - -# shows how serialization functions can be used on any string, how to transform values and output some other format -# read and decode zip file and start an interactive REPL -$ fq -i . <(curl -sL https://github.com/stefangabos/world_countries/archive/master.zip) -# select from interesting xml file -zip> .local_files[] | select(.file_name == "world_countries-master/data/countries/en/world.xml").uncompressed | repl -# convert xml into jq value -> .local_files[95].uncompressed string> from_xml | repl -# sort countries by and select the first one ->> object> .countries.country | sort_by(."@name") | first | repl -# see what current input is ->>> object> . -{ - "@alpha2": "af", - "@alpha3": "afg", - "@id": "4", - "@name": "Afghanistan" -} -# remove "@" prefix from keys and convert to YAML and print it ->>> object> with_entries(.key |= .[1:]) | to_yaml | print -alpha2: af -alpha3: afg -id: "4" -name: Afghanistan -# exit all REPLs back to shell ->>> object> ^D ->> object> ^D -> .local_files[95].uncompressed string> ^D -zip> ^D -``` - -- `from_xml`/`from_xml($opts)` Parse XML into jq value. `$opts` are: - - `{seq: true}` preserve element ordering if more than one sibling.
- - `{array: true}` use nested `[name, attributes, children]` arrays to represent elements. Attributes will be `null` if none and children will be `[]` if none, this is to make it easier to work with as the array as 3 values. `to_xml` does not require this.
-- `from_html`/`from_html($opts)` Parse HTML into jq value.
- Similar to `from_xml` but parses html5 in non-script mode. Will always have a `html` root with `head` and `body` elements.
- `$opts` are: - - `{array: true}` use nested arrays to represent elements.
- - `{seq: true}` preserve element ordering if more than one sibling.
-- `to_xml`/`to_xml($opts})` Serialize jq value into XML.
- Assumes object representation if input is an object, and nested arrays if input is an array.
- Will automatically add a root `doc` element if jq value has more then one root element.
- If a `#seq` is found on at least one element all siblings will be sort by sequence number. Attributes are always sorted.
- `$opts` are: - - `{indent: number}` indent child elements.
- - XML elements can be represented as jq value in two ways, as objects (inspired by [mxj](https://github.com/clbanning/mxj) and [xml.com's Converting Between XML and JSON -](https://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html)) or nested arrays. Both representations are lossy and might lose ordering of elements, text nodes and comments. In object representation `from_xml`, `from_html` and `to_xml` support `{seq:true}` option to parse/serialize `{"#seq"=}` attributes to preserve element sibling ordering. - - The object version is denser and convenient to query, the nested arrays version is probably easier to use when generating XML. - - Let's assume `$xml` is this XML document as a string: - ```xml - - - text - text - - ``` - - With object representation an element is represented as: - - Attributes as `@` prefixed `@` keys. - - Text nodes as `#text`. - - Comment nodes as `#comment` keys. - - For explicit sibling ordering `#seq` keys with a number, can be negative, assumed zero if missing. - - Child element with only text as `` key with text as value. - - Child element with more than just text as `` key with value an object. - - Multiple child element siblings with same name as `name` key with value as array with strings and objects. - ```jq - > $xml | from_xml - { - "doc": { - "child": [ - { - "@attr": "1" - }, - { - "#text": "text", - "@attr": "2" - } - ], - "other": "text" - } - } - ``` - - With nested array representation, an array with these values `["", {attributes...}, [children...]]` - - Index 0 is an element name. - - Index 1 object attributes (including `#text` and `#comment` keys). - - Index 2 array of child elements. - # - ```jq - > $xml | from_xml({array: true}) - [ - "doc", - [ - [ - "child", - { - "attr": "1" - } - ], - [ - "child", - { - "#text": "text", - "attr": "2" - } - ], - [ - "other", - { - "#text": "text" - } - ] - ] - ] - ``` - Parse and include `#seq` attributes if needed: - ```jq - > $xml | from_xml({seq:true}) - { - "doc": { - "child": [ - { - "#seq": 0, - "@attr": "1" - }, - { - "#seq": 1, - "#text": "text", - "@attr": "2" - } - ], - "other": { - "#seq": 2, - "#text": "text" - } - } - } - ```` - Select values in ``, remove ``, add a `` element, serialize to xml with 2 space indent and print the string - ```jq - > $xml | from_xml.doc | del(.child) | .new = "abc" | {root: .} | to_xml({indent: 2}) | println - - abc - text - - ``` - -JSON and jq-flavoured JSON -- `fromjson` Parse JSON into jq value. -- `tojson`/`tojson($opts)` Serialize jq value into JSON. `$opts` are: - - `{indent: number}` Indent depth. -- `from_jq` Parse jq-flavoured JSON into jq value. -- `to_jq`/`to_jq($opts)` Serialize jq value into jq-flavoured JSON. jq-flavoured JSON has optional key quotes, `#` comments and can have trailing comma in arrays. `$opts` are: - - `{indent: number}` Indent depth. -- `from_jsonl` Parse JSON lines into jq array. -- `to_jsonl` Serialize jq array into JSONL. - -Note that `fromjson` and `tojson` use different naming conventions as they originate from jq's standard library. - -YAML -- `from_yaml` Parse YAML into jq value. -- `to_yaml`/`to_yaml($opts)` Serialize jq value into YAML. `$opts` are: - - `{indent: number}` Indent depth. - -TOML -- `from_toml` Parse TOML into jq value. -- `to_toml`/`to_toml($opts)` Serialize jq value into TOML. `$opts` are: - - `{indent: number}` Indent depth. - -CSV -- `from_csv`/`from_cvs($opts)` Parse CSV into jq value.
- To work with tab separated values you can use `fromcvs({comma: "\t"})` or `fq -d csv -o 'comma="\t"'`
- `$opts` are: - - `{comma: string}` field separator, default ",".
- - `{comment: string}` comment line character, default "#".
-- `to_csv`/`to_csv($opts)` Serialize jq value into CSV. `$opts` are: - - `{comma: string}` field separator, default ",".
- -XML encoding -- `from_xmlentities` Decode XML entities. -- `to_xmlentities` Encode XML entities. - -URL parts and XML encodings -- `from_urlpath` Decode URL path component. -- `to_urlpath` Encode URL path component. Whitespace as %20. -- `from_urlencode` Decode URL query encoding. -- `to_urlencode` Encode URL to query encoding. Whitespace as "+". -- `from_urlquery` Decode URL query into object. For duplicates keys value will be an array. -- `to_urlquery` Encode object into query string. -- `from_url` Decode URL into object. - ```jq - > "schema://user:pass@host/path?key=value#fragment" | from_url - { - "fragment": "fragment", - "host": "host", - "path": "/path", - "query": { - "key": "value" - }, - "rawquery": "key=value", - "scheme": "schema", - "user": { - "password": "pass", - "username": "user" - } - } - ``` -- `to_url` Encode object into URL string. - -Binary encodings like hex and base64 -- `from_hex` Decode hex string to binary. -- `to_hex` Encode binary into hex string. -- `from_base64`/`from_base64($opts)` Decode base64 encodings into binary. `$opts` are: - - `{encoding:string}` encoding variant: `std` (default), `url`, `rawstd` or `rawurl` -- `to_base64`/`to_base64($opts)` Encode binary into base64 encodings. `$opts` are: - - `{encoding:string}` encoding variant: `std` (default), `url`, `rawstd` or `rawurl` - -Hash functions -- `to_md4` Hash binary using md4. -- `to_md5` Hash binary using md5. -- `to_sha1` Hash binary using sha1. -- `to_sha256` Hash binary using sha256. -- `to_sha512` Hash binary using sha512. -- `to_sha3_224` Hash binary using sha3 224. -- `to_sha3_256` Hash binary using sha3 256. -- `to_sha3_384` Hash binary using sha3 384. -- `to_sha3_512` Hash binary using sha3 512. - -Text encodings -- `to_iso8859_1` Decode binary as ISO8859-1 into string. -- `from_iso8859_1` Encode string as ISO8859-1 into binary. -- `to_utf8` Encode string as UTF8 into binary. -- `from_utf8` Decode binary as UTF8 into string. -- `to_utf16` Encode string as UTF16 into binary. -- `from_utf16` Decode binary as UTF16 into string. -- `to_utf16le` Encode string as UTF16 little-endian into binary. -- `from_utf16le` Decode binary as UTF16 little-endian into string. -- `to_utf16be` Encode string as UTF16 big-endian into binary. -- `from_utf16be` Decode binary as UTF16 big-endian into string. - -## Options - -fq has some general options in addition to decode and decoders specific options. They all use the same `-o =` argument. - -`` is fuzzily parsed based on the type of the option. Ex: a string can be specified as `-o name=string` or `-o name="string"`. - -### `-o bits_format=` - -How to represent raw binary as JSON. - -- `-o bits_format=base64` Base64 string. -- `-o bits_format=byte_array` Array of bytes (zero bit padded if size is not byte aligned). -- `-o bits_format=hex` Hex string. -- `-o bits_format=md5` MD5 hex string (zero bit padded). -- `-o bits_format=snippet` Truncated Base64 string prefixed with bit length. -- `-o bits_format=string` String with raw bytes (zero bit padded if size is not byte aligned). The string is binary safe internally in fq but bytes not representable as UTF-8 will be lost if turn into JSON (default). -- `-p bits_format=truncate` Truncated string. - -```sh -$ fq -V -o bits_format=base64 . file -``` -In query -```jq -tovalue({bits_format: "md5"}) -``` - -### `-o skip_gaps=` - -Skip gaps fields (`gap0` etc) when using `tovalue` or `-V`. Note that this might affect array indexes if one more more gaps fields are skipped in an array. - -```sh -$ fq -V -o skip_gaps=true . file -``` -In query -```jq -tovalue({skip_gaps: true}) -``` - -### `-o array_truncate=` - -By default truncate long array when displaying decode value tree. Use `dd` or `d({array_truncate: 0})` to not truncate. - -### `-o string_truncate=` - -By default truncate long strings when displaying decode value tree. Use `dd` or `d({string_truncate: 0})` to not truncate. - - -## Color and unicode output - -fq by default tries to use colors if possible, this can be disabled with `-M`. You can also -enable usage of unicode characters for improved output by setting the environment -variable `CLIUNICODE`. - -## Configuration - -To add own functions you can use `init.jq` that will be read from -- `$HOME/Library/Application Support/fq` or `$HOME/.config/fq` on macOS -- `$HOME/.config/fq` on Linux, BSD etc -- `%AppData%` on Windows - -## Use as script interpreter - -fq can be used as a script interpreter: - -`mp3_duration.jq`: -```jq -#!/usr/bin/env fq -d mp3 -rf -[.frames[].header | .sample_count / .sample_rate] | add -``` - -## Decoded values - -When decoding something, using `decode` or `mp3` etc, you a decode value is returned. They behave like -normal jq values but has special abilities and is used to represent the decoded structure. Each value -always has a name, type and a bit range. - -A value has these special keys (TODO: remove, are internal) - -- `_actual` decoded (not symbol mapped value) -- `_bits` bits in range as a binary -- `_buffer_root` first decode value for current buffer -- `_bytes` bits in range as binary using byte units -- `_description` description of value (optional) -- `_error` error message (optional) -- `_format` name of decoded format (optional, only format root) -- `_format_root` first decode value for current format -- `_gap` is a bit range gap (was not decoded) -- `_index` index in parent array (only for values in arrays) -- `_len` bit range length (TODO: rename) -- `_name` name of value -- `_out` decoded out value -- `_parent` parent decode value -- `_path` jq path to decode value -- `_root` root decode value -- `_start` bit range start -- `_stop` bit range stop -- `_sym` symbolic value (optional) - -## Own decoders and use as library - -TODO - -## Known issues and useful tricks - -### Run interactive mode with no input -```sh -fq -i -null> -``` - -### `select` fails with `expected an ... but got: ...` - -Try add `select(...)?` to catch and ignore type errors in the select expression. - -### Manual decode - -Sometimes fq fails to decode or you know there is valid data buried inside some binary or maybe -you know the format of some gap field. Then you can decode manually. - -
-# try decode a `mp3_frame` that failed to decode
-$ fq -d mp3 '.gap0 | mp3_frame' file.mp3
-# skip first 10 bytes then decode as `mp3_frame`
-$ fq -d bytes '.[10:] | mp3_frame' file.mp3
-
- -### Use `.` as input and in a positional argument - -The expression `.a | f(.b)` might not work as expected. `.` is `.a` when evaluating the arguments so -the positional argument will end up being `.a.b`. Instead do `. as $c | .a | f($c.b)`. - -### Building array is slow - -Try to use `map` or `foreach` to avoid rebuilding the whole array for each append. - -### Use `print` and `println` to produce more friendly compact output - -``` -> [[0,"a"],[1,"b"]] -[ - [ - 0, - "a" - ], - [ - 1, - "b" - ] -] -> [[0,"a"],[1,"b"]] | .[] | "\(.[0]): \(.[1])" | println -0: a -1: b -``` - -### `repl` argument using function or variable causes `variable not defined` - -`true as $verbose | repl({verbose: $verbose})` will currently fail as `repl` is -implemented by rewriting the query to `map(true as $verbose | .) | repl({verbose: $verbose})`. - -### `error` produces no output - -`null | error` behaves as `empty`. diff --git a/format/apple/bookmark/apple_bookmark.md b/format/apple/bookmark/apple_bookmark.md index 76aa4152a..44b52c346 100644 --- a/format/apple/bookmark/apple_bookmark.md +++ b/format/apple/bookmark/apple_bookmark.md @@ -2,7 +2,7 @@ Apple's `bookmarkData` format is used to encode information that can be resolved into a `URL` object for a file even if the user moves or renames it. Can also contain security scoping information for App Sandbox support. -These `bookmarkData` blobs are often found endcoded in data fields of Binary +These `bookmarkData` blobs are often found encoded in data fields of Binary Property Lists. Notable examples include: - `com.apple.finder.plist` - contains an `FXRecentFolders` value, which is an diff --git a/format/apple/bookmark/testdata/help_applebookmark.fqtest b/format/apple/bookmark/testdata/help_applebookmark.fqtest index 32fcc824e..adbebac96 100644 --- a/format/apple/bookmark/testdata/help_applebookmark.fqtest +++ b/format/apple/bookmark/testdata/help_applebookmark.fqtest @@ -12,7 +12,7 @@ Decode examples Apple's bookmarkData format is used to encode information that can be resolved into a URL object for a file even if the user moves or renames it. Can also contain security scoping information for App Sandbox support. -These bookmarkData blobs are often found endcoded in data fields of Binary Property Lists. Notable examples include: +These bookmarkData blobs are often found encoded in data fields of Binary Property Lists. Notable examples include: - com.apple.finder.plist - contains an FXRecentFolders value, which is an array of ten objects, each of which consists of a name and file-bookmark field, which is a bookmarkData object for each recently accessed folder location. diff --git a/format/apple/bplist/bplist.md b/format/apple/bplist/bplist.md index bfdf9c36b..31f881a7a 100644 --- a/format/apple/bplist/bplist.md +++ b/format/apple/bplist/bplist.md @@ -7,8 +7,8 @@ $ fq d Info.plist Timestamps in Apple Binary Property Lists are encoded as Cocoa Core Data timestamps, where the raw value is the floating point number of seconds since January 1, 2001. By default, `fq` will render the raw floating point value. In -order to get the raw value or string description, use the `todescription` -function, you can use the `tovalue` and `todescription` functions: +order to get the raw value or the string description, use the `tovalue` or +`todescription` functions: ```sh $ fq 'torepr.SomeTimeStamp | tovalue' Info.plist diff --git a/format/asn1/asn1_ber.md b/format/asn1/asn1_ber.md index 5a49f3066..ce4506ecc 100644 --- a/format/asn1/asn1_ber.md +++ b/format/asn1/asn1_ber.md @@ -19,7 +19,7 @@ $ fq -d asn1_ber '.constructed[1].value | asn1_ber' file.ber ### Manual schema ```sh -$ fq -d asn1_ber 'torepr as $r | ["version", "modulus", "private_exponent", "private_exponen", "prime1", "prime2", "exponent1", "exponent2", "coefficient"] | with_entries({key: .value, value: $r[.key]})' pkcs1.der +$ fq -d asn1_ber 'torepr as $r | ["version", "modulus", "private_exponent", "prime1", "prime2", "exponent1", "exponent2", "coefficient"] | with_entries({key: .value, value: $r[.key]})' pkcs1.der ``` ### References diff --git a/format/asn1/testdata/help_asn1_ber.fqtest b/format/asn1/testdata/help_asn1_ber.fqtest index c12c0882d..09e6d54d8 100644 --- a/format/asn1/testdata/help_asn1_ber.fqtest +++ b/format/asn1/testdata/help_asn1_ber.fqtest @@ -25,7 +25,7 @@ Can decode nested values Manual schema ============= - $ fq -d asn1_ber 'torepr as $r | ["version", "modulus", "private_exponent", "private_exponen", "prime1", "prime2", "exponent1", "exponent2", "coefficient"] | with_entries({key: .value, value: $r[.key]})' pkcs1.der + $ fq -d asn1_ber 'torepr as $r | ["version", "modulus", "private_exponent", "prime1", "prime2", "exponent1", "exponent2", "coefficient"] | with_entries({key: .value, value: $r[.key]})' pkcs1.der References ========== diff --git a/format/bits/bits.md b/format/bits/bits.md index f682f583d..b037305fd 100644 --- a/format/bits/bits.md +++ b/format/bits/bits.md @@ -12,7 +12,7 @@ $ echo 'some {"a":1} json' | fq -d bits '.[40:-48] | fromjson' ## Index bits ```sh -✗ echo 'hello' | fq -d bits '.[4]' +$ echo 'hello' | fq -d bits '.[4]' 1 $ echo 'hello' | fq -c -d bits '[.[range(8)]]' [0,1,1,0,1,0,0,0] diff --git a/format/fit/fit.md b/format/fit/fit.md index 136ecd069..5ef7ed9f0 100644 --- a/format/fit/fit.md +++ b/format/fit/fit.md @@ -2,7 +2,7 @@ - Fields with subcomponents, such as "compressed_speed_distance" field on globalMessageNumber 20 is not represented correctly. The field is read as 3 separate bytes where the first 12 bits are speed and the last 12 bits are distance. -- There are still lots of UNKOWN fields due to gaps in Garmins SDK Profile documentation. (Currently FIT SDK 21.126) +- There are still lots of UNKNOWN fields due to gaps in Garmin's SDK Profile documentation. (Currently FIT SDK 21.126) - Compressed timestamp messages are not accumulated against last known full timestamp. ### Convert stream of data messages to JSON array diff --git a/format/format.go b/format/format.go index 1b077e299..a38e5c225 100644 --- a/format/format.go +++ b/format/format.go @@ -452,11 +452,11 @@ type TLS_In struct { } type Pg_Control_In struct { - Flavour string `doc:"PostgreSQL flavour: postgres14, pgproee14.., postgres10"` + Flavour string `doc:"PostgreSQL flavour: postgres14, pgproee14, postgres10"` } type Pg_Heap_In struct { - Flavour string `doc:"PostgreSQL flavour: postgres14, pgproee14.., postgres10"` + Flavour string `doc:"PostgreSQL flavour: postgres14, pgproee14, postgres10"` Page int `doc:"First page number in file, default is 0"` Segment int `doc:"Segment file number (16790.1 is 1), default is 0"` } diff --git a/format/isobmff/mp4.md b/format/isobmff/mp4.md index 0181ce3a7..5b8058e24 100644 --- a/format/isobmff/mp4.md +++ b/format/isobmff/mp4.md @@ -1,7 +1,7 @@ ### Speed up decoding by not decoding samples ```sh -# manually decode first sample as a aac_frame +# manually decode first sample as an aac_frame $ fq -o decode_samples=false '.tracks[0].samples[0] | aac_frame | d' file.mp4 ``` @@ -23,14 +23,14 @@ $ fq 'del(.tracks) | grep_by(.type=="mdat").data = "" | tovalue' file. $ fq -n '"AAAAHGVsc3QAAAAAAAAAAQAAADIAAAQAAAEAAA==" | from_base64 | mp4({force:true}) | d' ``` -### Lookup mp4 box using a mp4 box path. +### Lookup mp4 box using an mp4 box path ```sh # | mp4_path($path) -> $ fq 'mp4_path(".moov.trak[1]")' file.mp4 ``` -### Get mp4 box path for a decode value box. +### Get mp4 box path for a decode value box ```sh # | mp4_path -> string diff --git a/format/isobmff/testdata/help_mp4.fqtest b/format/isobmff/testdata/help_mp4.fqtest index cc9b1f72d..dcd2e0789 100644 --- a/format/isobmff/testdata/help_mp4.fqtest +++ b/format/isobmff/testdata/help_mp4.fqtest @@ -22,7 +22,7 @@ Decode examples Speed up decoding by not decoding samples ========================================= - # manually decode first sample as a aac_frame + # manually decode first sample as an aac_frame $ fq -o decode_samples=false '.tracks[0].samples[0] | aac_frame | d' file.mp4 Entries for first edit list as values @@ -37,13 +37,13 @@ Force decode a single box ========================= $ fq -n '"AAAAHGVsc3QAAAAAAAAAAQAAADIAAAQAAAEAAA==" | from_base64 | mp4({force:true}) | d' -Lookup mp4 box using a mp4 box path. +Lookup mp4 box using an mp4 box path ==================================== # | mp4_path($path) -> $ fq 'mp4_path(".moov.trak[1]")' file.mp4 -Get mp4 box path for a decode value box. -======================================== +Get mp4 box path for a decode value box +======================================= # | mp4_path -> string $ fq 'grep_by(.type == "trak") | mp4_path' file.mp4 diff --git a/format/markdown/markdown.jq b/format/markdown/markdown.jq index 2b8ec8535..a18ccd5e5 100644 --- a/format/markdown/markdown.jq +++ b/format/markdown/markdown.jq @@ -92,3 +92,42 @@ def _markdown_to_text($width; $header_depth): ][:-1] | join("\n"); def _markdown_to_text: _markdown_to_text(-1; 0); + +def _markdown_to_asciidoc: + def _f: + def _collect: + ( [.children[] | _f] + | join("") + ); + if type == "string" then gsub("\n"; " ") + elif .type == "document" then .children[] | _f + elif .type == "heading" then + ( (.children | map(_markdown_children_to_text(0)) | join(" ")) as $h + | "" + , $h + ":::" + , "" + ) + elif .type == "paragraph" then _collect + elif .type | . == "em" or . == "strong" then "_" + _collect + "_" + elif .type == "link" then + ( _collect as $text + | if $text == .destination then $text + else "\(.destination)[\($text)]" + end + ) + elif .type == "code_block" then + ( .literal + | rtrimstr("\n") + | "[source,console]" + , "----" + , . + , "----" + ) + elif .type == "code" then "`" + .literal + "`" + elif .type == "list" then "", ([.children[] | _f] | join("\n")), "", "//-" # TODO: delim + elif .type == "list_item" then "\(.bullet_char) \(.children[] | _f)" + elif .type == "html_span" then .literal | gsub("
"; "\n") # TODO: more? + else empty + end; + [_f] | join("\n"); + diff --git a/format/matroska/matroska.md b/format/matroska/matroska.md index e533de5d1..aef0fdd0a 100644 --- a/format/matroska/matroska.md +++ b/format/matroska/matroska.md @@ -1,7 +1,7 @@ ### Lookup element using path ```sh -$ fq 'matroska_path(".Segment.Tracks[0)")' file.mkv +$ fq 'matroska_path(".Segment.Tracks[0]")' file.mkv ``` ### Get path to element diff --git a/format/matroska/testdata/help_matroska.fqtest b/format/matroska/testdata/help_matroska.fqtest index 550ab65e8..a0868cc55 100644 --- a/format/matroska/testdata/help_matroska.fqtest +++ b/format/matroska/testdata/help_matroska.fqtest @@ -20,7 +20,7 @@ Decode examples Lookup element using path ========================= - $ fq 'matroska_path(".Segment.Tracks[0)")' file.mkv + $ fq 'matroska_path(".Segment.Tracks[0]")' file.mkv Get path to element =================== diff --git a/format/midi/midi.md b/format/midi/midi.md index 58031a90b..62ffe76ef 100644 --- a/format/midi/midi.md +++ b/format/midi/midi.md @@ -8,7 +8,7 @@ 1. Extract the track names from a MIDI file ``` -fq -d midi -d midi '.. | select(.event=="track_name")? | "\(.track_name)"' midi/twinkle.mid +fq -d midi '.. | select(.event=="track_name")? | "\(.track_name)"' midi/twinkle.mid ``` 2. Extract the tempo changes from a MIDI file diff --git a/format/midi/testdata/help_midi.fqtest b/format/midi/testdata/help_midi.fqtest index 319b3f700..1a616540c 100644 --- a/format/midi/testdata/help_midi.fqtest +++ b/format/midi/testdata/help_midi.fqtest @@ -19,7 +19,7 @@ Sample queries ============== * Extract the track names from a MIDI file - fq -d midi -d midi '.. | select(.event=="track_name")? | "\(.track_name)"' midi/twinkle.mid + fq -d midi '.. | select(.event=="track_name")? | "\(.track_name)"' midi/twinkle.mid * Extract the tempo changes from a MIDI file diff --git a/format/riff/avi.md b/format/riff/avi.md index 395cddb3b..4d90e6cd8 100644 --- a/format/riff/avi.md +++ b/format/riff/avi.md @@ -15,7 +15,7 @@ $ fq -o decode_samples=false '[.chunks[0] | grep_by(.id=="LIST" and .type=="strl ### Speed up decoding by disabling sample and extended chunks decoding -If your not interested in sample details or extended chunks you can speed up decoding by using: +If you're not interested in sample details or extended chunks you can speed up decoding by using: ```sh $ fq -o decode_samples=false -o decode_extended_chunks=false d file.avi ``` diff --git a/format/riff/testdata/help_avi.fqtest b/format/riff/testdata/help_avi.fqtest index 2f0bc3b82..d8aec01c8 100644 --- a/format/riff/testdata/help_avi.fqtest +++ b/format/riff/testdata/help_avi.fqtest @@ -34,7 +34,7 @@ Show stream summary Speed up decoding by disabling sample and extended chunks decoding ================================================================== -If your not interested in sample details or extended chunks you can speed up decoding by using: +If you're not interested in sample details or extended chunks you can speed up decoding by using: $ fq -o decode_samples=false -o decode_extended_chunks=false d file.avi diff --git a/format/rtmp/rtmp.md b/format/rtmp/rtmp.md index 3478c7066..675f33bed 100644 --- a/format/rtmp/rtmp.md +++ b/format/rtmp/rtmp.md @@ -1,4 +1,4 @@ -Current only supports plain RTMP (not RTMPT or encrypted variants etc) with AMF0 (not AMF3). +Currently only supports plain RTMP (not RTMPT or encrypted variants etc) with AMF0 (not AMF3). ### Show rtmp streams in PCAP file ```sh diff --git a/format/rtmp/testdata/help_rtmp.fqtest b/format/rtmp/testdata/help_rtmp.fqtest index 5afb3486e..84cda9b67 100644 --- a/format/rtmp/testdata/help_rtmp.fqtest +++ b/format/rtmp/testdata/help_rtmp.fqtest @@ -9,7 +9,7 @@ Decode examples # Decode value as rtmp ... | rtmp -Current only supports plain RTMP (not RTMPT or encrypted variants etc) with AMF0 (not AMF3). +Currently only supports plain RTMP (not RTMPT or encrypted variants etc) with AMF0 (not AMF3). Show rtmp streams in PCAP file ============================== diff --git a/format/tls/testdata/help_tls.fqtest b/format/tls/testdata/help_tls.fqtest index f2b73c0c7..459f8fa93 100644 --- a/format/tls/testdata/help_tls.fqtest +++ b/format/tls/testdata/help_tls.fqtest @@ -18,16 +18,16 @@ Decode examples # Decode value as tls ... | tls({keylog:""}) -Supports decoding of most standard records, messages and extensions. Can also decrypt most standard cipher suits in a PCAP with +Supports decoding of most standard records, messages and extensions. Can also decrypt most standard cipher suites in a PCAP with traffic in both directions if a NSS key log is provided. -Decode and decrypt provding a PCAP and key log -============================================== +Decode and decrypt providing a PCAP and key log +=============================================== Write traffic to a PCAP file: $ tcpdump -i -w traffic.pcap -Make sure your curl TLS backend support SSLKEYLOGFILE and do: +Make sure your curl TLS backend supports SSLKEYLOGFILE and do: $ SSLKEYLOGFILE=traffic.keylog curl --tls-max 1.2 https://host/path @@ -59,24 +59,19 @@ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_RC4_128_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, -TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, -TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, -TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384eadAESGCM, +TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, +TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, -TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, -TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, -TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, -TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, -TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, -TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_PSK_WITH_AES_128_CBC_SHA, -TLS_PSK_WITH_AES_256_CBC_SHA, TLS_PSK_WITH_RC4_128_SHA, TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, TLS_RSA_EXPORT_WITH_RC4_40_MD5, -TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, -TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, -TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_256_GCM_SHA384, -TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_RC4_128_MD5, TLS_RSA_WITH_RC4_128_SHA, -TLS_RSA_WITH_RC4_128_SHA +TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, +TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, +TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, +TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, +TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_PSK_WITH_AES_128_CBC_SHA, TLS_PSK_WITH_AES_256_CBC_SHA, TLS_PSK_WITH_RC4_128_SHA, +TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, TLS_RSA_EXPORT_WITH_RC4_40_MD5, TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, +TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA256, +TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_RC4_128_MD5, TLS_RSA_WITH_RC4_128_SHA References ========== - RFC 5246: The Transport Layer Security (TLS) Protocol (https://www.rfc-editor.org/rfc/rfc5246) -- RFC 6101: The Secure Sockets Layer (SSL) Protocol Version 3.0 (https://www.rfc-editor.org/rfc/rfc) +- RFC 6101: The Secure Sockets Layer (SSL) Protocol Version 3.0 (https://www.rfc-editor.org/rfc/rfc6101) diff --git a/format/tls/tls.md b/format/tls/tls.md index 8b9521b5c..2ba37c3c5 100644 --- a/format/tls/tls.md +++ b/format/tls/tls.md @@ -1,6 +1,6 @@ -Supports decoding of most standard records, messages and extensions. Can also decrypt most standard cipher suits in a PCAP with traffic in both directions if a NSS key log is provided. +Supports decoding of most standard records, messages and extensions. Can also decrypt most standard cipher suites in a PCAP with traffic in both directions if a NSS key log is provided. -### Decode and decrypt provding a PCAP and key log +### Decode and decrypt providing a PCAP and key log Write traffic to a PCAP file: @@ -8,7 +8,7 @@ Write traffic to a PCAP file: $ tcpdump -i -w traffic.pcap ``` -Make sure your curl TLS backend support `SSLKEYLOGFILE` and do: +Make sure your curl TLS backend supports `SSLKEYLOGFILE` and do: ```sh $ SSLKEYLOGFILE=traffic.keylog curl --tls-max 1.2 https://host/path ``` @@ -69,58 +69,44 @@ $ fq -o keylog=@traffic.keylog 'first(grep_by(.server.stream | format == "tls") `TLS_ECDH_RSA_WITH_RC4_128_SHA`, `TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA`, `TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA`, -`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA`, `TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256`, `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`, `TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA`, -`TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA`, `TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384`, -`TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384eadAESGCM`, +`TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`, `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256`, `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305`, `TLS_ECDHE_ECDSA_WITH_RC4_128_SHA`, -`TLS_ECDHE_ECDSA_WITH_RC4_128_SHA`, `TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA`, `TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256`, `TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA`, `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA`, `TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA`, `TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256`, `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`, `TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA`, -`TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA`, `TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384`, `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`, `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256`, `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305`, `TLS_ECDHE_RSA_WITH_RC4_128_SHA`, -`TLS_ECDHE_RSA_WITH_RC4_128_SHA`, `TLS_PSK_WITH_AES_128_CBC_SHA`, `TLS_PSK_WITH_AES_256_CBC_SHA`, `TLS_PSK_WITH_RC4_128_SHA`, `TLS_RSA_EXPORT_WITH_DES40_CBC_SHA`, `TLS_RSA_EXPORT_WITH_RC4_40_MD5`, `TLS_RSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_RSA_WITH_3DES_EDE_CBC_SHA`, -`TLS_RSA_WITH_AES_128_CBC_SHA`, `TLS_RSA_WITH_AES_128_CBC_SHA`, `TLS_RSA_WITH_AES_128_CBC_SHA256`, -`TLS_RSA_WITH_AES_128_CBC_SHA256`, `TLS_RSA_WITH_AES_128_GCM_SHA256`, -`TLS_RSA_WITH_AES_128_GCM_SHA256`, -`TLS_RSA_WITH_AES_256_CBC_SHA`, `TLS_RSA_WITH_AES_256_CBC_SHA`, `TLS_RSA_WITH_AES_256_CBC_SHA256`, `TLS_RSA_WITH_AES_256_GCM_SHA384`, -`TLS_RSA_WITH_AES_256_GCM_SHA384`, `TLS_RSA_WITH_DES_CBC_SHA`, `TLS_RSA_WITH_RC4_128_MD5`, -`TLS_RSA_WITH_RC4_128_SHA`, `TLS_RSA_WITH_RC4_128_SHA` ### References - [RFC 5246: The Transport Layer Security (TLS) Protocol](https://www.rfc-editor.org/rfc/rfc5246) -- [RFC 6101: The Secure Sockets Layer (SSL) Protocol Version 3.0](https://www.rfc-editor.org/rfc/rfc) +- [RFC 6101: The Secure Sockets Layer (SSL) Protocol Version 3.0](https://www.rfc-editor.org/rfc/rfc6101) diff --git a/format/tzx/tzx.md b/format/tzx/tzx.md index 51792101e..108ab70a4 100644 --- a/format/tzx/tzx.md +++ b/format/tzx/tzx.md @@ -2,7 +2,7 @@ ZX Spectrum computers, although some specialized versions of the format have been defined for other machines such as the Amstrad CPC and C64. -The format was originally created by Tomaz Kac, who was maintainer until +The format was originally created by Tomaz Kac, who was the maintainer until `revision 1.13`, before passing it to Martijn v.d. Heide. For a brief period the company Ramsoft became the maintainers, and created revision `v1.20`. diff --git a/format/xml/html.md b/format/xml/html.md index 39d015866..b3240b20b 100644 --- a/format/xml/html.md +++ b/format/xml/html.md @@ -1,4 +1,4 @@ -HTML is decoded in HTML5 mode and will always include ``, `` and `` element. +HTML is decoded in HTML5 mode and will always include ``, `` and `` elements. See xml format for more examples and how to preserve element order and how to encode to xml. diff --git a/format/xml/testdata/help_html.fqtest b/format/xml/testdata/help_html.fqtest index 8dcd06616..ee29b8dba 100644 --- a/format/xml/testdata/help_html.fqtest +++ b/format/xml/testdata/help_html.fqtest @@ -20,7 +20,7 @@ Decode examples # Decode value as html ... | html({array:false,attribute_prefix:"@",seq:false}) -HTML is decoded in HTML5 mode and will always include , and element. +HTML is decoded in HTML5 mode and will always include , and elements. See xml format for more examples and how to preserve element order and how to encode to xml. diff --git a/format/xml/testdata/help_xml.fqtest b/format/xml/testdata/help_xml.fqtest index 9b11a4805..855f41a8f 100644 --- a/format/xml/testdata/help_xml.fqtest +++ b/format/xml/testdata/help_xml.fqtest @@ -20,11 +20,10 @@ Decode examples # Decode value as xml ... | xml({array:false,attribute_prefix:"@",seq:false}) -XML can be decoded and encoded into jq values in two ways, elements as object or array. Which variant to use depends a bit what you -want to do. The object variant might be easier to query for a specific value but array might be easier to use to generate xml or to -query after all elements of some kind etc. +XML can be decoded and encoded into jq values in two ways, elements as object or array. The object variant might be easier to query +for a specific value but array might be easier to use to generate xml or to query after all elements of some kind etc. -Encoding is done using the to_xml function and it will figure what variant that is used based on the input value. Is has two optional +Encoding is done using the to_xml function and it will figure what variant that is used based on the input value. It has two optional options indent and attribute_prefix. Elements as object @@ -75,7 +74,7 @@ order might be lost. Elements as array ================= -Elements are arrays of the shape ["#text": "body text", "attr_name", {key: "attr value"}|null, [, ...]]. +Elements are arrays of the shape ["name", null|{#text|attribute: "value"}, [, ...]]. # decode as array $ echo 'bbbccc' | fq -d xml -o array=true diff --git a/format/xml/xml.md b/format/xml/xml.md index 439844274..b40b3c271 100644 --- a/format/xml/xml.md +++ b/format/xml/xml.md @@ -1,10 +1,10 @@ XML can be decoded and encoded into jq values in two ways, elements as object or array. -Which variant to use depends a bit what you want to do. The object variant might be easier +The object variant might be easier to query for a specific value but array might be easier to use to generate xml or to query after all elements of some kind etc. Encoding is done using the `to_xml` function and it will figure what variant that is used based on the input value. -Is has two optional options `indent` and `attribute_prefix`. +It has two optional options `indent` and `attribute_prefix`. ### Elements as object @@ -56,7 +56,7 @@ $ echo 'bbbccc' | fq -r -d xml -o seq=true ### Elements as array -Elements are arrays of the shape `["#text": "body text", "attr_name", {key: "attr value"}|null, [, ...]]`. +Elements are arrays of the shape `["name", null|{#text|attribute: "value"}, [, ...]]`. ```sh # decode as array diff --git a/pkg/interp/help.jq b/pkg/interp/help.jq index a70c12ca2..601e41106 100644 --- a/pkg/interp/help.jq +++ b/pkg/interp/help.jq @@ -79,7 +79,7 @@ def help: help(null); def _help($arg0; $topic): ( $topic | if . == "usage" then - "Usage: \($arg0) [OPTIONS] [--] [EXPR] [FILE...]" + "Usage: \($arg0) [OPTIONS] [--] [EXPRESSION] [FILE...]" elif . == "example_usage" then ( "Example usages:" , " fq . file" diff --git a/pkg/interp/init.jq b/pkg/interp/init.jq index 50f736d2c..6ae021406 100644 --- a/pkg/interp/init.jq +++ b/pkg/interp/init.jq @@ -172,7 +172,9 @@ def _main: ) ) ); - ( . as {$version, $os, $arch, $go_version, $args, args: [$arg0]} + ( . as $main_input + | _main_input($main_input) as $_ + | . as {$version, $os, $arch, $go_version, $args, args: [$arg0]} # make sure we don't unintentionally use . to make things clearer | null | ( try _args_parse($args[1:]; _opt_cli_opts) diff --git a/pkg/interp/internal.jq b/pkg/interp/internal.jq index 461e26281..8cbfbe253 100644 --- a/pkg/interp/internal.jq +++ b/pkg/interp/internal.jq @@ -81,6 +81,9 @@ def _input_decode_errors(f): _global_var("input_decode_errors"; f); def _slurps: _global_var("slurps"); def _slurps(f): _global_var("slurps"; f); +def _main_input: _global_var("main_input"); +def _main_input(f): _global_var("main_input"; f ); + # call f and finally eval fin even if empty or error. # _finally(1; debug) # _finally(null; debug) diff --git a/pkg/interp/options.jq b/pkg/interp/options.jq index 615a8f69c..457685cee 100644 --- a/pkg/interp/options.jq +++ b/pkg/interp/options.jq @@ -53,14 +53,13 @@ def _opt_build_default_fixed: , expr: "." , filenames: null , force: false - , include_path: null + , include_path: [] , join_string: "\n" , null_input: false , raw_file: [] , raw_output: ($stdout.is_terminal | not) , raw_string: false , repl: false - , show_formats: false , show_help: false , sizebase: 10 , skip_gaps: false @@ -74,48 +73,59 @@ def _opt_build_default_fixed: ); def _opt_options: - { addrbase: "number" - , arg: "array_string_pair" - , args: "array_string" - , argdecode: "array_string_pair" - , argjson: "array_string_pair" - , argsjson: "array_string" - , array_truncate: "number" - , bits_format: "string" - , byte_colors: "csv_ranges_array" - , color: "boolean" - , colors: "csv_kv_obj" - , compact: "boolean" - , completion_timeout: "number" - , decode_group: "string" - , decode_progress: "boolean" - , depth: "number" - , display_bytes: "number" - , expr_eval_path: "string" - , expr_file: "string" - , expr_given: "boolean" - , expr: "string" - , filenames: "array_string" - , force: "boolean" - , include_path: "string" - , join_string: "string" - , line_bytes: "number" - , null_input: "boolean" - , raw_file: "array_string_pair" - , raw_output: "boolean" - , raw_string: "boolean" - , repl: "boolean" - , show_formats: "boolean" - , show_help: "boolean" - , sizebase: "number" - , skip_gaps: "boolean" - , slurp: "boolean" - , string_input: "boolean" - , string_truncate: "number" - , unicode: "boolean" - , value_output: "boolean" - , verbose: "boolean" - , width: "number" + { addrbase: {type: "number", description: "Number base for addresses"} + , arg: {type: "array_string_pair", description: "--arg pairs", internal: true} + , args: {type: "array_string", description: "--args arguments", internal: true} + , argdecode: {type: "array_string_pair", description: "--argdecode pairs", internal: true} + , argjson: {type: "array_string_pair", description: "--argjson pairs", internal: true} + , argsjson: {type: "array_string", description: "--argsjson arguments", internal :true} + , array_truncate: {type: "number", description: "Array display length to truncate"} + , bits_format: + { type: "string" + , description: "Raw bits representation" + , values: + { "base64": "Base64 string." + , "byte_array": "Array of bytes (zero bit padded if size is not byte aligned)." + , "hex": "Hex string." + , "md5": "MD5 hex string (zero bit padded)." + , "snippet": "Truncated Base64 string prefixed with bit length." + , "string": "String with raw bytes (zero bit padded if size is not byte aligned). The string is binary safe internally in fq but bytes not representable as UTF-8 will be lost if turned into JSON (default)." + , "truncate": "Truncated string." + } + } + , byte_colors: {type: "csv_ranges_array", description: "Byte value colorization"} + , color: {type: "boolean", description: "Use color"} + , colors: {type: "csv_kv_obj", description: "Color scheme"} + , compact: {type: "boolean", description: "Use compact JSON"} + , completion_timeout: {type: "number", description: "Seconds to wait for completion results"} + , decode_group: {type: "string", description: "Format to decode", internal: true} + , decode_progress: {type: "boolean", description: "Show decode progress", internal: true} + , depth: {type: "number", description: "Display tree depth limit"} + , display_bytes: {type: "number", description: "Display bytes limit"} + , expr_eval_path: {type: "string", description: "Where does expression come from", internal: true} + , expr_file: {type: "string", description: "Read expression from file", internal: true} + , expr_given: {type: "boolean", description: "An expression arg as given", internal: true} + , expr: {type: "string", description: "Expression to evaluate", internal: true} + , filenames: {type: "array_string", description: "Filenames arguments", internal: true} + , force: {type: "boolean", description: "Force decode"} + , include_path: {type: "array_string", description: "Include paths", internal: true} + , join_string: {type: "string", description: "String used to join outputs"} + , line_bytes: {type: "number", description: "Number of bytes per display line"} + , null_input: {type: "boolean", description: "Null input", internal: true} + , raw_file: {type: "array_string_pair", description: "--raw-file pairs", internal: true} + , raw_output: {type: "boolean", description: "Raw output is safe", internal: true} + , raw_string: {type: "boolean", description: "Raw string output"} + , repl: {type: "boolean", description: "Invole REPL", internal: true} + , show_help: {type: "boolean", description: "Show help", internal: true} + , sizebase: {type: "number", description: "Number base for sizes"} + , skip_gaps: {type: "boolean", description: "Skip gaps when representing decode value (arrays) as JSON"} + , slurp: {type: "boolean", description: "Slurp inputs into an array", internal: true} + , string_input: {type: "boolean", description: "Raw string input", internal: true} + , string_truncate: {type: "number", description: "String display length truncate"} + , unicode: {type: "boolean", description: "Use unicode"} + , value_output: {type: "boolean", description: "Output decode value as JSON", internal: true} + , verbose: {type: "boolean", description: "Verbose display"} + , width: {type: "number", description: "Terminal width"} }; def _opt_eval($rest): @@ -389,7 +399,7 @@ def _opt_cli_arg_to_options: ( _opt_options as $opts | with_entries( ( .key as $k - | .value |= _opt_to($opts[$k] // "fuzzy") + | .value |= _opt_to($opts[$k].type // "fuzzy") | select(.value != null) ) ) @@ -399,7 +409,8 @@ def _opt_cli_arg_from_options: ( _opt_options as $opts | with_entries( ( .key as $k - | .value |= _opt_from($opts[$k] // "string") + | select($opts[$k].internal | not) + | .value |= _opt_from($opts[$k].type // "string") | select(.value != null) ) ) @@ -408,19 +419,19 @@ def _opt_cli_arg_from_options: def _opt_cli_opts: { arg: { long: "--arg" - , description: "Set variable $NAME to string VALUE" + , description: "Set $NAME to string VALUE" , pairs: "NAME VALUE" } , argdecode: { long: "--argdecode" # TODO: remove at some point , aliases: ["--decode-file"] - , description: "Set variable $NAME to decode of PATH" + , description: "Set $NAME to decode of PATH" , pairs: "NAME PATH" } , argjson: { long: "--argjson" - , description: "Set variable $NAME to JSON" + , description: "Set $NAME to JSON" , pairs: "NAME JSON" } , args: @@ -436,7 +447,7 @@ def _opt_cli_opts: , compact: { short: "-c" , long: "--compact-output" - , description: "Compact output" + , description: "Use compact output" , bool: true } , color_output: @@ -454,7 +465,7 @@ def _opt_cli_opts: , expr_file: { short: "-f" , long: "--from-file" - , description: "Read EXPR from file" + , description: "Read EXPRESSION from file" , string: "PATH" } , show_help: @@ -473,7 +484,7 @@ def _opt_cli_opts: , include_path: { short: "-L" , long: "--include-path" - , description: "Include search path" + , description: "Add PATH to include search paths" , array: "PATH" } , null_output: @@ -486,7 +497,7 @@ def _opt_cli_opts: , null_input: { short: "-n" , long: "--null-input" - , description: "Null input (use input and inputs functions to read)" + , description: "Null input (use input and inputs to read)" , bool: true } , monochrome_output: @@ -499,7 +510,7 @@ def _opt_cli_opts: { short: "-o" , long: "--option" , description: "Set option (ex: -o color=true, see --help options)" - , object: "KEY=VALUE/@PATH", + , object: "NAME=VALUE/@PATH", } , string_input: { short: "-R" @@ -511,7 +522,7 @@ def _opt_cli_opts: { long: "--raw-file" # for jq compatibility , aliases: ["--raw-file"] - , description: "Set variable $NAME to string content of file" + , description: "Set $NAME to string content of file" , pairs: "NAME PATH" } , raw_string: diff --git a/pkg/interp/testdata/args.fqtest b/pkg/interp/testdata/args.fqtest index 7bc700bfa..6d1ab1cd2 100644 --- a/pkg/interp/testdata/args.fqtest +++ b/pkg/interp/testdata/args.fqtest @@ -1,7 +1,7 @@ $ fq exitcode: 2 stderr: -Usage: fq [OPTIONS] [--] [EXPR] [FILE...] +Usage: fq [OPTIONS] [--] [EXPRESSION] [FILE...] $ _STDIN_IS_TERMINAL=1 fq type "number" stdin: @@ -11,7 +11,7 @@ fq - jq for binary formats Tool, language and decoders for working with binary data. For more information see https://github.com/wader/fq -Usage: fq [OPTIONS] [--] [EXPR] [FILE...] +Usage: fq [OPTIONS] [--] [EXPRESSION] [FILE...] Example usages: fq . file @@ -24,30 +24,30 @@ Example usages: fq 'grep("^main$") | parent' /bin/ls fq -i ---arg NAME VALUE Set variable $NAME to string VALUE ---argdecode NAME PATH Set variable $NAME to decode of PATH ---argjson NAME JSON Set variable $NAME to JSON ---args Consume remaining arguments as positional strings ---color-output,-C Force color output ---compact-output,-c Compact output ---decode,-d NAME Decode format or group (probe) ---from-file,-f PATH Read EXPR from file ---help,-h [TOPIC] Show help for TOPIC (ex: -h formats, -h mp4) ---include-path,-L PATH Include search path ---join-output,-j No newline after each output ---jsonargs Consume remaining arguments as positional JSON ---monochrome-output,-M Force monochrome output ---null-input,-n Null input (use input and inputs functions to read) ---option,-o KEY=VALUE/@PATH Set option (ex: -o color=true, see --help options) ---raw-file NAME PATH Set variable $NAME to string content of file ---raw-input,-R Read raw input strings (don't decode) ---raw-output,-r Raw string output (without quotes) ---raw-output0 NUL (zero) byte after each output ---repl,-i Interactive REPL ---slurp,-s Slurp all inputs into an array or string (-Rs) ---unicode-output,-U Force unicode output ---value-output,-V Output JSON value (-Vr for raw string) ---version,-v Show version +--arg NAME VALUE Set $NAME to string VALUE +--argdecode NAME PATH Set $NAME to decode of PATH +--argjson NAME JSON Set $NAME to JSON +--args Consume remaining arguments as positional strings +--color-output,-C Force color output +--compact-output,-c Use compact output +--decode,-d NAME Decode format or group (probe) +--from-file,-f PATH Read EXPRESSION from file +--help,-h [TOPIC] Show help for TOPIC (ex: -h formats, -h mp4) +--include-path,-L PATH Add PATH to include search paths +--join-output,-j No newline after each output +--jsonargs Consume remaining arguments as positional JSON +--monochrome-output,-M Force monochrome output +--null-input,-n Null input (use input and inputs to read) +--option,-o NAME=VALUE/@PATH Set option (ex: -o color=true, see --help options) +--raw-file NAME PATH Set $NAME to string content of file +--raw-input,-R Read raw input strings (don't decode) +--raw-output,-r Raw string output (without quotes) +--raw-output0 NUL (zero) byte after each output +--repl,-i Interactive REPL +--slurp,-s Slurp all inputs into an array or string (-Rs) +--unicode-output,-U Force unicode output +--value-output,-V Output JSON value (-Vr for raw string) +--version,-v Show version $ fq -i null> ^D $ fq -i . test.mp3 @@ -70,11 +70,6 @@ $ fq -nc "[1,2,3]" [1,2,3] $ fq --help options addrbase 16 -arg [] -argdecode [] -argjson [] -args [] -argsjson [] array_truncate 50 bits_format string byte_colors 0-255=default+bold,0=brightblack,32-126:9-13=default @@ -82,33 +77,16 @@ color false colors array=default,dumpaddr=yellow,dumpheader=yellow+underline,error=brightred,false=yellow,index=default,null=brightblack,number=cyan,object=default,objectkey=brightblue,prompt_repl_level=brightblack,prompt_value=default,string=green,true=yellow,value=default compact false completion_timeout 10 -decode_group probe -decode_progress false depth 0 display_bytes 16 -expr . -expr_eval_path arg -expr_file -expr_given false -filenames [null] force false -include_path join_string \n line_bytes 16 -null_input false -raw_file [] -raw_output false raw_string false -repl false -show_formats false -show_help options sizebase 10 skip_gaps false -slurp false -string_input false string_truncate 50 unicode false -value_output false verbose false width 135 $ fq -X diff --git a/pkg/interp/testdata/exitcode.fqtest b/pkg/interp/testdata/exitcode.fqtest index 17c3affac..dc69822bc 100644 --- a/pkg/interp/testdata/exitcode.fqtest +++ b/pkg/interp/testdata/exitcode.fqtest @@ -1,7 +1,7 @@ $ fq exitcode: 2 stderr: -Usage: fq [OPTIONS] [--] [EXPR] [FILE...] +Usage: fq [OPTIONS] [--] [EXPRESSION] [FILE...] $ fq -n '1+"a"' exitcode: 5 stderr: diff --git a/pkg/interp/testdata/options.fqtest b/pkg/interp/testdata/options.fqtest index 82b8dddc5..40dc3b3d9 100644 --- a/pkg/interp/testdata/options.fqtest +++ b/pkg/interp/testdata/options.fqtest @@ -74,7 +74,7 @@ $ fq -n options null ], "force": false, - "include_path": null, + "include_path": [], "join_string": "\n", "line_bytes": 16, "null_input": true, @@ -82,7 +82,6 @@ $ fq -n options "raw_output": false, "raw_string": false, "repl": false, - "show_formats": false, "show_help": false, "sizebase": 10, "skip_gaps": false, @@ -153,7 +152,7 @@ $ fq -o 'filenames=["test.mp3"]' format $ fq -o 'force=true' -n options.force true $ fq -o include_path=path -n options.include_path -"path" +[] $ fq -o 'join_string=aaa\n' -n options.join_string "aaa\n"aaa $ fq -o line_bytes=true -n options.line_bytes