1
1
mirror of https://github.com/wader/fq.git synced 2024-08-18 00:10:29 +03:00

Compare commits

...

28 Commits

Author SHA1 Message Date
Mattias Wadman
2505c3ef9a
Merge 9c1c3c3fce into 19bd2fbd7c 2024-06-10 17:52:50 +03:00
Mattias Wadman
19bd2fbd7c
Merge pull request #962 from wader/bump-gomod-golang-x-net-0.26.0
Update gomod-golang-x-net to 0.26.0 from 0.25.0
2024-06-05 19:33:31 +02:00
bump
c55e10667c Update gomod-golang-x-net to 0.26.0 from 0.25.0
Tags https://github.com/golang/net/tags
2024-06-05 19:24:19 +02:00
Mattias Wadman
292bd02397
Merge pull request #960 from wader/bump-docker-golang-1.22.4
Update docker-golang to 1.22.4 from 1.22.3
2024-06-05 18:55:09 +02:00
Mattias Wadman
bbdd43f5c8
Merge pull request #964 from wader/bump-github-go-version-1.22.4
Update github-go-version to 1.22.4 from 1.22.3
2024-06-05 18:54:26 +02:00
Mattias Wadman
35a8eddf04
Merge pull request #963 from wader/bump-gomod-golang-x-term-0.21.0
Update gomod-golang-x-term to 0.21.0 from 0.20.0
2024-06-05 18:54:09 +02:00
Mattias Wadman
8df50bf62f
Merge pull request #961 from wader/bump-gomod-golang-x-crypto-0.24.0
Update gomod-golang-x-crypto to 0.24.0 from 0.23.0
2024-06-05 18:27:04 +02:00
bump
1ff5a3fadf Update github-go-version to 1.22.4 from 1.22.3 2024-06-05 16:04:19 +00:00
bump
7566fd9307 Update gomod-golang-x-term to 0.21.0 from 0.20.0
Tags https://github.com/golang/term/tags
2024-06-05 16:04:16 +00:00
bump
f7cbf84469 Update gomod-golang-x-crypto to 0.24.0 from 0.23.0
Tags https://github.com/golang/crypto/tags
2024-06-05 16:04:11 +00:00
bump
ab09d3ce6f Update docker-golang to 1.22.4 from 1.22.3 2024-06-05 16:04:06 +00:00
Mattias Wadman
262d7e1fa1
Merge pull request #958 from wader/bump-gomod-golang/text-0.16.0
Update gomod-golang/text to 0.16.0 from 0.15.0
2024-06-04 18:17:04 +02:00
bump
8bc1a20b2a Update gomod-golang/text to 0.16.0 from 0.15.0
Source diff 0.15.0..0.16.0 https://github.com/golang/text/compare/v0.15.0..v0.16.0
2024-06-04 16:04:04 +00:00
Mattias Wadman
b0025b64c9
Merge pull request #956 from wader/bump-make-golangci-lint-1.59.0
Update make-golangci-lint to 1.59.0 from 1.58.2
2024-05-27 18:31:46 +02:00
Mattias Wadman
2171924898
Merge pull request #957 from wader/bump-github-golangci-lint-1.59.0
Update github-golangci-lint to 1.59.0 from 1.58.2
2024-05-27 18:31:40 +02:00
bump
44e2385ace Update github-golangci-lint to 1.59.0 from 1.58.2
Release notes https://github.com/golangci/golangci-lint/releases/tag/v1.59.0
2024-05-27 16:03:45 +00:00
bump
0cd90ce0de Update make-golangci-lint to 1.59.0 from 1.58.2
Release notes https://github.com/golangci/golangci-lint/releases/tag/v1.59.0
2024-05-27 16:03:43 +00:00
Mattias Wadman
9775381e05
Merge pull request #955 from wader/doc-formats-description
doc: Include format description per format
2024-05-24 12:20:04 +02:00
Mattias Wadman
6f2b59944d doc: Include format description per format 2024-05-24 12:08:07 +02:00
Mattias Wadman
cb1557dd65
Merge pull request #954 from wader/usage-rework2
doc: Fix function indent
2024-05-23 19:46:09 +02:00
Mattias Wadman
b818923c26 doc: Fix function indent 2024-05-23 18:33:37 +02:00
Mattias Wadman
e97e915352
Merge pull request #953 from wader/usage-rework
doc: Reorganize and cleanup function descriptions
2024-05-23 18:32:10 +02:00
Mattias Wadman
dff7c3cd74
Merge pull request #952 from wader/bump-gomod-BurntSushi/toml-1.4.0
Update gomod-BurntSushi/toml to 1.4.0 from 1.3.2
2024-05-23 18:20:50 +02:00
Mattias Wadman
40f38a5558 doc: Reorganize and cleanup function descriptions 2024-05-23 18:20:10 +02:00
bump
61f81fbfda Update gomod-BurntSushi/toml to 1.4.0 from 1.3.2
Source diff 1.3.2..1.4.0 https://github.com/BurntSushi/toml/compare/v1.3.2..v1.4.0
2024-05-23 16:04:49 +00:00
Mattias Wadman
ff65812941
Merge pull request #951 from wader/doc-add-kodsnack
doc: Add kodsnack 585 - Polymorfisk JSON
2024-05-22 10:47:35 +02:00
Mattias Wadman
175661d349 doc: Add kodsnack 585 - Polymorfisk JSON 2024-05-22 09:44:05 +02:00
Mattias Wadman
9c1c3c3fce pdf: Add decoder 2023-05-17 18:38:47 +02:00
13 changed files with 652 additions and 106 deletions

View File

@ -7,7 +7,7 @@ on:
pull_request:
env:
GOLANGCILINT_VERSION: "1.58.2"
GOLANGCILINT_VERSION: "1.59.0"
jobs:
lint:
@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/setup-go@v3
with:
go-version: "1.22.3"
go-version: "1.22.4"
- uses: actions/checkout@v3
- uses: golangci/golangci-lint-action@v3
with:
@ -47,7 +47,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: "1.22.3"
go-version: "1.22.4"
- name: Test
env:
GOARCH: ${{ matrix.goarch }}

View File

@ -15,7 +15,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: "1.22.3"
go-version: "1.22.4"
- uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser

View File

@ -1,5 +1,5 @@
# bump: docker-golang /FROM golang:([\d.]+)/ docker:golang|^1
FROM golang:1.22.3-bookworm AS base
FROM golang:1.22.4-bookworm AS base
# expect is used to test cli
RUN \

View File

@ -61,7 +61,7 @@ gogenerate: always
lint: always
# bump: make-golangci-lint /golangci-lint@v([\d.]+)/ git:https://github.com/golangci/golangci-lint.git|^1
# bump: make-golangci-lint link "Release notes" https://github.com/golangci/golangci-lint/releases/tag/v$LATEST
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.58.2 run
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.59.0 run
depgraph.svg: always
go run github.com/kisielk/godepgraph@latest github.com/wader/fq | dot -Tsvg -o godepgraph.svg

View File

@ -169,6 +169,7 @@ For details see [formats.md](doc/formats.md) and [usage.md](doc/usage.md).
## Presentations
- [Kodsnack 585 - Polymorfisk JSON](https://kodsnack.se/585/) - Swedish podcast episode about jq and fq
- "fq - jq for binary formats" at [FOSDEM 2023](https://fosdem.org/2023/) - [video & slides](https://fosdem.org/2023/schedule/event/bintools_fq/)
- "fq - jq for binary formats" at [No time to wait 6](https://mediaarea.net/NoTimeToWait6) - [video](https://www.youtube.com/watch?v=-Pwt5KL-xRs&t=1450s) - [slides](doc/presentations/nttw6/fq-nttw6-slides.pdf)
- "fq - jq for binary formats" at [Binary Tools Summit 2022](https://binary-tools.net/summit.html) - [video](https://www.youtube.com/watch?v=GJOq_b0eb-s&list=PLTj8twuHdQz-JcX7k6eOwyVPDB8CyfZc8&index=1) - [slides](doc/presentations/bts2022/fq-bts2022-v1.pdf)

View File

@ -70,6 +70,7 @@ def formats_sections:
| ({} | _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

View File

@ -157,6 +157,7 @@ fq -d bytes 'mp4({force: true})' file.mp4
[fq -rn -L . 'include "formats"; formats_sections']: sh-start
## aac_frame
Advanced Audio Coding frame.
### Options
@ -177,6 +178,7 @@ Decode value as aac_frame
```
## 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
@ -218,6 +220,7 @@ torepr' <sfl2 file>
- 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).
@ -250,6 +253,7 @@ $ fq -d asn1_ber 'torepr as $r | ["version", "modulus", "private_exponent", "pri
- https://lapo.it/asn1js/
## avc_au
H.264/AVC Access Unit.
### Options
@ -270,6 +274,7 @@ Decode value as avc_au
```
## avi
Audio Video Interleaved.
### Options
@ -318,6 +323,7 @@ $ fq -o decode_samples=false -o decode_extended_chunks=false d file.avi
- [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.
@ -337,6 +343,7 @@ xentripetal@fastmail.com
[@xentripetal](https://github.com/xentripetal)
## bencode
BitTorrent bencoding.
### Convert represented value to JSON
@ -348,6 +355,7 @@ $ fq -d bencode torepr file.torrent
- https://wiki.theory.org/BitTorrentSpecification#Bencoding
## bitcoin_block
Bitcoin block.
### Options
@ -368,6 +376,7 @@ Decode value as bitcoin_block
```
## bits
Raw bits.
Decode to a slice and indexable binary of bits.
@ -390,6 +399,7 @@ $ echo 'hello' | fq -c -d bits '[.[range(8)]]'
```
## bplist
Apple Binary Property List.
### Show full decoding
```sh
@ -454,6 +464,7 @@ bplist> from_ns_keyed_archiver(1)
- https://opensource.apple.com/source/CF/CF-550/CFBinaryPList.c
## bson
Binary JSON.
### Limitations
@ -479,6 +490,7 @@ $ fq -d bson 'torepr | select(.name=="bob")' file.bson
- https://bsonspec.org/spec.html
## bytes
Raw bytes.
Decode to a slice and indexable binary of bytes.
@ -506,6 +518,7 @@ $ echo 'hello' | fq -d bytes '.[1]'
```
## caff
Live2D Cubism archive.
### Options
@ -529,6 +542,7 @@ Decode value as caff
- [@ronsor](https://github.com/ronsor)
## cbor
Concise Binary Object Representation.
### Convert represented value to JSON
@ -541,6 +555,7 @@ $ fq -d cbor torepr file.cbor
- https://www.rfc-editor.org/rfc/rfc8949.html
## csv
Comma separated values.
### Options
@ -574,6 +589,7 @@ $ fq -d csv '.[0] as $t | .[1:] | map(with_entries(.key = $t[.key]))' file.csv
```
## fit
Garmin Flexible and Interoperable Data Transfer.
### Limitations
@ -596,6 +612,7 @@ $ fq '[.data_records[] | select(.record_header.message_type == "data").data_mess
- https://developer.garmin.com/fit/cookbook/decoding-activity-files/
## flac_frame
FLAC frame.
### Options
@ -616,6 +633,7 @@ Decode value as flac_frame
```
## hevc_au
H.265/HEVC Access Unit.
### Options
@ -636,6 +654,7 @@ Decode value as hevc_au
```
## html
HyperText Markup Language.
### Options
@ -719,6 +738,7 @@ $ fq -r -o array=true -d html '.. | select(.[0] == "a" and .[1].href)?.[1].href'
```
## leveldb_descriptor
LevelDB Descriptor.
### Limitations
@ -735,6 +755,7 @@ $ fq -r -o array=true -d html '.. | select(.[0] == "a" and .[1].href)?.[1].href'
- https://github.com/google/leveldb/blob/main/db/version_edit.cc
## leveldb_log
LevelDB Log.
### Limitations
@ -751,6 +772,7 @@ $ fq -r -o array=true -d html '.. | select(.[0] == "a" and .[1].href)?.[1].href'
- https://github.com/google/leveldb/blob/main/db/write_batch.cc
## leveldb_table
LevelDB Table.
### Limitations
@ -768,6 +790,7 @@ $ fq -r -o array=true -d html '.. | select(.[0] == "a" and .[1].href)?.[1].href'
- https://github.com/google/leveldb/blob/main/doc/index.md
## luajit
LuaJIT 2.0 bytecode.
### Authors
- [@dlatchx](https://github.com/dlatchx)
@ -777,6 +800,7 @@ $ fq -r -o array=true -d html '.. | select(.[0] == "a" and .[1].href)?.[1].href'
- http://scm.zoomquiet.top/data/20131216145900/index.html
## macho
Mach-O macOS executable.
Supports decoding vanilla and FAT Mach-O binaries.
@ -795,12 +819,14 @@ 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
@ -840,11 +866,13 @@ $ fq 'grep_by(.id == "Tracks") | matroska_path' file.mkv
- https://wiki.xiph.org/MatroskaOpus
## moc3
MOC3 file.
### Authors
- [@ronsor](https://github.com/ronsor)
## mp3
MP3 file.
### Options
@ -867,6 +895,7 @@ Decode value as mp3
```
## mp4
ISOBMFF, QuickTime and similar.
### Options
@ -932,6 +961,7 @@ $ fq 'grep_by(.type == "trak") | mp4_path' file.mp4
- [Quicktime file format](https://developer.apple.com/standards/qtff-2001.pdf)
## msgpack
MessagePack.
### Convert represented value to JSON
@ -943,6 +973,7 @@ $ fq -d msgpack torepr file.msgpack
- https://github.com/msgpack/msgpack/blob/master/spec.md
## nes
iNES/NES 2.0 cartridge ROM format.
### Limitations
@ -979,6 +1010,7 @@ $ for line in $(fq -r '.chr_rom[] | nes_tokitty(5)' file.nes);do printf "%b%s" "
- https://bugzmanov.github.io/nes_ebook/chapter_6_3.html
## opentimestamps
OpenTimestamps file.
### View a full OpenTimestamps file
@ -1006,6 +1038,7 @@ $ fq '.operations | map(select(.attestation_type == "bitcoin")) | length > 0' fi
- https://github.com/opentimestamps/python-opentimestamps
## pcap
PCAP packet capture.
### Build object with number of (reassembled) TCP bytes sent to/from client IP
```sh
@ -1018,6 +1051,7 @@ $ fq '.tcp_connections | group_by(.client.ip) | map({key: .[0].client.ip, value:
}
```
## pg_btree
PostgreSQL btree index file.
### Options
@ -1057,6 +1091,7 @@ p.n.safonov@gmail.com
### References
- https://www.postgresql.org/docs/current/storage-page-layout.html
## pg_control
PostgreSQL control file.
### Options
@ -1096,6 +1131,7 @@ p.n.safonov@gmail.com
### References
- https://github.com/postgres/postgres/blob/REL_14_2/src/include/catalog/pg_control.h
## pg_heap
PostgreSQL heap file.
### Options
@ -1148,6 +1184,7 @@ p.n.safonov@gmail.com
### References
- https://www.postgresql.org/docs/current/storage-page-layout.html
## protobuf
Protobuf.
### Can decode sub messages
@ -1159,6 +1196,7 @@ $ fq -d protobuf '.fields[6].wire_value | protobuf | d' file
- 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).
@ -1172,6 +1210,7 @@ fq '.tcp_connections[] | select(.server.port=="rtmp") | d' file.cap
- https://rtmp.veriskope.com/pdf/video_file_format_spec_v10.pdf
## tls
Transport layer security.
### Options
@ -1319,6 +1358,7 @@ $ fq -o keylog=@traffic.keylog 'first(grep_by(.server.stream | format == "tls")
- [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
@ -1339,6 +1379,7 @@ fq '.v2plusdatablock.leap_second_records | length' tziffile
- https://datatracker.ietf.org/doc/html/rfc8536
## wasm
WebAssembly Binary Format.
### Count opcode usage
```sh
@ -1359,6 +1400,7 @@ $ fq '.sections | {import: map(select(.id == "import_section").content.im.x[].nm
- https://webassembly.github.io/spec/core/
## xml
Extensible Markup Language.
### Options
@ -1487,6 +1529,7 @@ $ echo '<a><b/><b>bbb</b><c attr="value">ccc</c></a>' | fq -o array=true '.[2][2
- [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

View File

@ -406,8 +406,6 @@ There is also `tobitsrange` and `tobytesrange` which does the same thing but wil
Both `.[index]` and `.[start:end]` support negative indices to index from end.
TODO: tobytesrange, padding
#### 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:
@ -428,92 +426,175 @@ Some examples:
`[(.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`.
The difference between `tobits` and `tobytes` is
TODO: padding and alignment
## Functions
All decode functions are available in two forms, just `<format>` (like `mp3`) that returns a decode value on error and `from_<format>` which throws error on decode error.
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: <value from a>, b: <value from 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("<name>")`
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("<name>")`
Output previously slurped values.
#### `spew`
Outputs all slurps as an object. `spew("<name>")` 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 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 has own options that can be specificed as part of `$opts` or as `-o name=value`. Too 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("<format>")`, `decode("<format>"; $opts)`
Decode format.
#### `probe`, `probe($opts)`
Probe and decode format.
#### `<format>`, `<format>($opts)`
Same as `decode("<format>")` and `decode("<format>"; $opts)`. Decode as format and return decode value even on decode error.
#### `from_<format>`, `from_<format>($opts)`
Same as `decode("<format>")` and `decode("<format>"; $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 `<function-name>/<arity>`. Same function names with different arity are treated as separate functions, but are usually related in some way in practice.
### Function added in fq
#### `print`, `println`, `printerr`, `printerrln`
Print string or if not a string compact JSON value to stdout or stderr.
- All standard library functions from jq
- Adds a few new general functions:
- `print`, `println`, `printerr`, `printerrln` prints to stdout and stderr.
- `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.
- `debug(f)` like `debug` but uses arg to produce a debug message. `{a: 123} | debug({a}) | ...`.
- `path_to_expr` from `["key", 1]` to `".key[1]"`.
- `expr_to_path` from `".key[1]"` to `["key", 1]`.
- `diff($a; $b)` produce diff object between two values.
- `delta`, `delta_by(f)`, array with difference between all consecutive pairs.
- `chunk(f)`, split array or string into even chunks
- Bitwise functions `band`, `bor`, `bxor`, `bsl`, `bsr` and `bnot`. Works the same as jq math functions,
unary uses input and if more than one argument all as arguments ignoring the input. Ex: `1 | bnot` `bsl(1; 3)`
- Adds some decode value specific functions:
- `root` tree root for value
- `buffer_root` root value of buffer for value
- `format_root` root value of format for value
- `parent` parent value
- `parents` output parents of value
- `topath` path of value. Use `path_to_expr` to get a string representation.
- `tovalue`, `tovalue($opts)` symbolic value if available otherwise actual value
- `toactual`, `toactual($opts)` actual value (usually the decoded value)
- `tosym`, `tosym($opts)` symbolic value (mapped etc)
- `todescription` description of value
- `torepr` converts decode value into what it represents. For example convert msgpack decode value
into a value representing its JSON representation.
- 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
- String functions are not overloaded to support binary for now as some of them might have behaviors that might be confusing.
- `explode` is overloaded to work with binary. Will explode into array of the unit of the binary.
end of binary.
instead of possibly multi-byte UTF-8 codepoints. This allows to match raw bytes. Ex: `match("\u00ff"; "b")`
will match the byte `0xff` and not the UTF-8 encoded codepoint for 255, `match("[^\u00ff]"; "b")` will match
all non-`0xff` bytes.
- `grep` functions take 1 or 2 arguments. First is a scalar to match, where a string is
treated as a regexp. A binary will match exact bytes. Second argument are regexp
flags with addition that "b" will treat each byte in the input binary as a code point, this
makes it possible to match exact bytes.
- `grep($v)`, `grep($v; $flags)` recursively match value and binary
- `vgrep($v)`, `vgrep($v; $flags)` recursively match value
- `bgrep($v)`, `bgrep($v; $flags)` recursively match binary
- `fgrep($v)`, `fgrep($v; $flags)` recursively match field name
- `grep_by(f)` recursively match using a filter. Ex: `grep_by(. > 180 and . < 200)`, `first(grep_by(format == "id3v2"))`.
- Binary:
- `tobits` - Transform input to binary with bit as unit, does not preserve source range, will start at zero.
- `tobitsrange` - Transform input to binary with bit as unit, preserves source range if possible.
- `tobytes` - Transform input to binary with byte as unit, does not preserve source range, will start at zero.
- `tobytesrange` - Transform input binary with byte as unit, preserves source range if possible.
- `.[start:end]`, `.[:end]`, `.[start:]` - Slice binary from start to end preserve source range.
- `open` open file for reading
- All decode functions take an optional option argument. The only 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 currently have to do `fq -d bytes 'mp3({force: true})' file`.
- `decode`, `decode("<format>")`, `decode("<format>"; $opts)` decode format
- `probe`, `probe($opts)` probe and decode format
- `mp3`, `mp3($opts)`, ..., `<format>`, `<format>($opts)` same as `decode("<format>")`, `decode("<format>"; $opts)` decode as format and return decode value even on decode error.
- `from_mp3`, `from_mp3($opts)`, ..., `from_<format>`, `from_<format>($opts)` same as `decode("<format>")`, `decode("<format>"; $opts)` decode as format but throw error on decode error.
- Display shows hexdump/ASCII/tree 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
- `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("<name>")` 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("<name>")` output previously slurped values. `spew` outputs all slurps as an object, `spew("<name>")` 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.
#### `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

View File

@ -46,6 +46,7 @@ import (
_ "github.com/wader/fq/format/opentimestamps"
_ "github.com/wader/fq/format/opus"
_ "github.com/wader/fq/format/pcap"
_ "github.com/wader/fq/format/pdf"
_ "github.com/wader/fq/format/png"
_ "github.com/wader/fq/format/postgres"
_ "github.com/wader/fq/format/prores"

View File

@ -154,6 +154,7 @@ var (
Opus_Packet = &decode.Group{Name: "opus_packet"}
PCAP = &decode.Group{Name: "pcap"}
PCAPNG = &decode.Group{Name: "pcapng"}
PDF = &decode.Group{Name: "pdf"}
Pg_BTree = &decode.Group{Name: "pg_btree"}
Pg_Control = &decode.Group{Name: "pg_control"}
Pg_Heap = &decode.Group{Name: "pg_heap"}

418
format/pdf/pdf.go Normal file
View File

@ -0,0 +1,418 @@
package pdf
// https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf
// https://feliam.wordpress.com/2010/08/14/pdf-a-broken-spec/
// https://github.com/modesty/pdf2json
// TODO: parse-from-end if possible?
// TODO: more unescape?
// TODO: streams filters
// TODO: refs
// TODO: EOL between object number generation "obj"?
// Ex:
// 202 0
// obj
// endobj
import (
"bytes"
"encoding/hex"
"errors"
"io"
"log"
"regexp"
"strconv"
"strings"
"github.com/wader/fq/format"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp"
"github.com/wader/fq/pkg/scalar"
"golang.org/x/text/encoding/charmap"
"golang.org/x/text/encoding/unicode"
)
var imageFormat decode.Group
func init() {
interp.RegisterFormat(
format.PDF,
&decode.Format{
Description: "Portable Document Format",
Groups: []*decode.Group{format.Probe},
DecodeFn: pdfDecode,
Dependencies: []decode.Dependency{
{Groups: []*decode.Group{format.Image}, Out: &imageFormat},
},
})
}
var eofLine = `%%EOF`
var whitespaceChars = []byte(" \n\r")
var delimiterChars = []byte(" \n\r()<>[]{}/%")
func isWhitespace(b byte) bool { return bytes.IndexByte(whitespaceChars, b) != -1 }
// \r, \n or \r\n
// TODO: figure someting nicer
func findLineEnd(d *decode.D) int64 {
p := d.Pos()
ei, v, err := d.TryPeekFind(8, 8, -1, func(v uint64) bool {
return v == '\n' || v == '\r'
})
if errors.Is(err, io.EOF) {
// to support %%EOF not having a line ending
eofLineLen := int64(len(eofLine))
if d.BitsLeft()*8 >= eofLineLen {
return eofLineLen
}
return -1
}
if ei == -1 {
return ei
}
ei += 8
d.SeekRel(ei)
if v == '\r' && d.PeekUintBits(8) == '\n' {
ei += 8
}
d.SeekAbs(p)
return ei / 8
}
var strToNumber = scalar.StrFn(func(s scalar.Str) (scalar.Str, error) {
if strings.Contains(s.Actual, ".") {
// real
n, err := strconv.ParseFloat(s.Actual, 64)
if err != nil {
return s, err
}
s.Sym = n
} else {
// integer
n, err := strconv.ParseInt(s.Actual, 10, 64)
if err != nil {
return s, err
}
s.Sym = n
}
return s, nil
})
func decodeLineStr(d *decode.D) string {
ei := findLineEnd(d)
if ei == -1 {
d.Errorf("could not find line ending")
}
return strings.TrimRight(d.UTF8(int(ei)), "\r\n")
}
func decodeStrWhitespace(d *decode.D) string {
ei, _ := d.PeekFind(8, 8, -1, func(v uint64) bool {
return !isWhitespace(byte(v))
})
if ei == -1 {
d.Errorf("could not find line ending")
}
return d.UTF8(int(ei / 8))
}
func decodeStrUntil(b []byte) func(d *decode.D) string {
return func(d *decode.D) string {
ei, _ := d.PeekFind(8, 8, -1, func(v uint64) bool {
return bytes.IndexByte(b, byte(v)) != -1
})
if ei == -1 {
d.Errorf("could not find ending %d", b)
}
return d.UTF8(int(ei / 8))
}
}
var utf16BEExpect = unicode.UTF16(unicode.BigEndian, unicode.ExpectBOM)
var pdfDocEncoding = charmap.ISO8859_1 // TODO: correc?
// TODO: unescape \N \NN \NNN?
func decodeText(bs []byte) string {
if s, err := utf16BEExpect.NewDecoder().String(string(bs)); err == nil {
return s
}
if s, err := pdfDocEncoding.NewDecoder().String(string(bs)); err == nil {
return s
}
return string(bs)
}
// "\" + "\n" or "\r" or "\r\n"
var eolEscapeRe = regexp.MustCompile("\\\\(?:\n|\r|\r\n)")
func decodeStrLiteralString(d *decode.D) string {
ei, _ := d.PeekFind(16, 8, -1, func(v uint64) bool {
c1 := v >> 8
c0 := v & 0xff
// find non-escaped ")"
return c0 != '\\' && c1 == ')'
})
if ei == -1 {
d.Errorf("could not find literal string ending")
}
bs := d.BytesLen(int(ei / 8))
bs = eolEscapeRe.ReplaceAll(bs, []byte{})
return decodeText(bs)
}
var whitespaceRE = regexp.MustCompile(`\s`)
func decodeStrHexString(d *decode.D) string {
ei, _ := d.PeekFind(8, 8, -1, func(v uint64) bool {
return v == '>'
})
if ei == -1 {
d.Errorf("could not find literal string ending")
}
s := d.UTF8(int(ei / 8))
// whitespace should be ignored inside hex string
hexStr := whitespaceRE.ReplaceAllString(s, "")
buf, err := hex.DecodeString(hexStr)
if err != nil {
d.IOPanic(err, "hex.DecodeString")
}
return decodeText(buf)
}
func decodeValue(d *decode.D) any {
var r any
if isWhitespace(byte(d.PeekUintBits(8))) {
d.FieldStrFn("heading_whitespace", decodeStrWhitespace)
}
// currently only returns value for types whose values we are interested in, atm
// only dictionary, name, string and number so we can get stream length and filter
s := string(d.PeekBytes(2))
switch {
case (s[0] >= '0' && s[0] <= '9') || s[0] == '+' || s[0] == '-':
num := d.FieldStrFn("value", decodeStrUntil(delimiterChars), strToNumber)
r = num
case s == "<<":
d.FieldUTF8("start", 2)
d.FieldArray("pairs", func(d *decode.D) {
pairs := map[string]any{}
for string(d.PeekBytes(2)) != ">>" {
d.FieldStruct("pair", func(d *decode.D) {
var keyV any
var valueV any
d.FieldStruct("key", func(d *decode.D) { keyV = decodeValue(d) })
d.FieldStruct("value", func(d *decode.D) { valueV = decodeValue(d) })
if s, ok := keyV.(string); ok {
pairs[s] = valueV
}
})
}
r = pairs
})
d.FieldUTF8("end", 2)
case s[0] == '[':
d.FieldUTF8("start", 1)
d.FieldArray("objects", func(d *decode.D) {
var objectsV []any
for d.PeekUintBits(8) != ']' {
var valueV any
d.FieldStruct("object", func(d *decode.D) { valueV = decodeValue(d) })
objectsV = append(objectsV, valueV)
}
r = objectsV
})
d.FieldUTF8("end", 1)
case s[0] == '/':
d.FieldUTF8("start", 1)
name := d.FieldStrFn("value", decodeStrUntil(delimiterChars))
r = name
case s[0] == '<':
d.FieldUTF8("start", 1)
s := d.FieldStrFn("value", decodeStrHexString)
d.FieldUTF8("end", 1)
r = s
case s[0] == '(':
d.FieldUTF8("start", 1)
s := d.FieldStrFn("value", decodeStrLiteralString)
d.FieldUTF8("end", 1)
r = s
case s[0] == 'R':
// TODO: should handle references differently?
d.FieldUTF8("reference", 1)
case s[0] == 't':
d.FieldUTF8("value", 4, scalar.StrSym(true))
case s[0] == 'f':
d.FieldUTF8("value", 5, scalar.StrSym(false))
case s[0] == 'n':
d.FieldUTF8("value", 4, scalar.StrSym(nil))
default:
d.Fatalf("unknown type %q", s)
}
if isWhitespace(byte(d.PeekUintBits(8))) {
d.FieldStrFn("tailing_whitespace", decodeStrWhitespace)
}
return r
}
func pdfObjBodyDecode(d *decode.D) {
d.FieldValueStr("type", "body")
d.FieldArray("objects", func(d *decode.D) {
d.FieldStruct("object", func(d *decode.D) {
d.FieldStrFn("start", decodeLineStr)
var dictV any
d.FieldStruct("dictionary", func(d *decode.D) { dictV = decodeValue(d) })
log.Printf("dictV: %#+v\n", dictV)
endObj := false
for !endObj {
e := d.Pos()
line := decodeLineStr(d)
d.SeekAbs(e)
switch line {
case "endobj":
d.FieldStrFn("end", decodeLineStr)
endObj = true
case "stream":
d.FieldStruct("stream", func(d *decode.D) {
d.FieldStrFn("start", decodeLineStr)
// TODO: proper string find
ei, _ := d.PeekFind(64, 8, -1, func(v uint64) bool {
return v == (0 |
'e'<<56 |
'n'<<48 |
'd'<<40 |
's'<<32 |
't'<<24 |
'r'<<16 |
'e'<<8 |
'a')
})
// _ = d.FieldRawLen("data", ei)
if dv, _, _ := d.TryFieldFormatLen("data", ei, &imageFormat, nil); dv == nil {
_ = d.FieldRawLen("data", ei)
}
// d.FieldFormatReaderLen("uncompressed", dataLen, zlib.NewReader, iccProfileFormat)
// var rFn func(r io.Reader) io.Reader
// switch compressionMethod {
// case delfateMethod:
// *bitio.Buffer implements io.ByteReader so hat deflate don't do own
// // buffering and might read more than needed messing up knowing compressed size
// rFn = func(r io.Reader) io.Reader { return flate.NewReader(r) }
// // }
// if rFn != nil {
// readCompressedSize, uncompressedBB, dv, _, err := d.TryFieldReaderRangeFormat("uncompressed", d.Pos(), ei, rFn, imageFormat, nil)
// log.Printf("err: %#+v\n", err)
// if uncompressedBB != nil {
// if dv == nil {
// d.FieldRootBitBuf("uncompressed", uncompressedBB)
// }
// d.FieldRawLen("compressed", readCompressedSize)
// }
// }
d.FieldStrFn("end", decodeLineStr)
})
default:
d.Fatalf("bla")
}
}
})
})
}
func pdfDecode(d *decode.D) any {
d.FieldStruct("header", func(d *decode.D) {
d.FieldUTF8("version", 8, d.StrAssert(
"%PDF-1.0",
"%PDF-1.1",
"%PDF-1.2",
"%PDF-1.3",
"%PDF-1.4",
"%PDF-1.5",
"%PDF-1.6",
"%PDF-1.7",
))
d.SeekAbs(0)
// TODO: has binary comment, byte >= 128
d.FieldArray("comments", func(d *decode.D) {
for d.PeekUintBits(8) == '%' {
d.FieldStrFn("comment", decodeLineStr)
}
})
})
d.FieldArray("parts", func(d *decode.D) {
for !d.End() {
p := d.Pos()
line := decodeLineStr(d)
d.SeekAbs(p)
switch {
case strings.HasPrefix(line, "%"):
d.FieldStruct("comment", func(d *decode.D) {
d.FieldValueStr("type", "comment")
d.FieldStrFn("line", decodeLineStr)
})
case strings.TrimSpace(line) == "":
d.FieldStruct("whitespace", func(d *decode.D) {
d.FieldValueStr("type", "whitespace")
d.FieldStrFn("line", decodeLineStr)
})
case strings.HasSuffix(line, "obj"):
d.FieldStruct("body", pdfObjBodyDecode)
case line == "xref":
d.FieldStruct("xref", func(d *decode.D) {
d.FieldValueStr("type", "xref")
d.FieldStrFn("start", decodeLineStr)
d.FieldArray("lines", func(d *decode.D) {
for {
b := byte(d.PeekUintBits(8))
if !(b >= '0' && b <= '9') {
break
}
d.FieldStrFn("start", decodeLineStr)
}
})
})
case line == "trailer":
d.FieldStruct("trailer", func(d *decode.D) {
d.FieldValueStr("type", "trailer")
d.FieldStrFn("start", decodeLineStr)
d.FieldStruct("dictionary", func(d *decode.D) { decodeValue(d) })
})
case line == "startxref":
d.FieldStruct("startxref", func(d *decode.D) {
d.FieldValueStr("type", "startxref")
d.FieldStrFn("start", decodeLineStr)
d.FieldStrFn("offset", decodeLineStr, strToNumber)
})
default:
d.Fatalf("unknown line %q", line)
}
}
})
return nil
}

12
go.mod
View File

@ -9,7 +9,7 @@ require (
// bump: gomod-BurntSushi/toml /github\.com\/BurntSushi\/toml v(.*)/ https://github.com/BurntSushi/toml.git|^1
// bump: gomod-BurntSushi/toml command go get -d github.com/BurntSushi/toml@v$LATEST && go mod tidy
// bump: gomod-BurntSushi/toml link "Source diff $CURRENT..$LATEST" https://github.com/BurntSushi/toml/compare/v$CURRENT..v$LATEST
github.com/BurntSushi/toml v1.3.2
github.com/BurntSushi/toml v1.4.0
// bump: gomod-creasty-defaults /github\.com\/creasty\/defaults v(.*)/ https://github.com/creasty/defaults.git|^1
// bump: gomod-creasty-defaults command go get -d github.com/creasty/defaults@v$LATEST && go mod tidy
@ -48,7 +48,7 @@ require (
// bump: gomod-golang-x-crypto /golang\.org\/x\/crypto v(.*)/ https://github.com/golang/crypto.git|^0
// bump: gomod-golang-x-crypto command go get -d golang.org/x/crypto@v$LATEST && go mod tidy
// bump: gomod-golang-x-crypto link "Tags" https://github.com/golang/crypto/tags
golang.org/x/crypto v0.23.0
golang.org/x/crypto v0.24.0
// has no tags
// go get -d golang.org/x/exp@master && go mod tidy
@ -57,17 +57,17 @@ require (
// bump: gomod-golang-x-net /golang\.org\/x\/net v(.*)/ https://github.com/golang/net.git|^0
// bump: gomod-golang-x-net command go get -d golang.org/x/net@v$LATEST && go mod tidy
// bump: gomod-golang-x-net link "Tags" https://github.com/golang/net/tags
golang.org/x/net v0.25.0
golang.org/x/net v0.26.0
// bump: gomod-golang-x-term /golang\.org\/x\/term v(.*)/ https://github.com/golang/term.git|^0
// bump: gomod-golang-x-term command go get -d golang.org/x/term@v$LATEST && go mod tidy
// bump: gomod-golang-x-term link "Tags" https://github.com/golang/term/tags
golang.org/x/term v0.20.0
golang.org/x/term v0.21.0
// bump: gomod-golang/text /golang\.org\/x\/text v(.*)/ https://github.com/golang/text.git|^0
// bump: gomod-golang/text command go get -d golang.org/x/text@v$LATEST && go mod tidy
// bump: gomod-golang/text link "Source diff $CURRENT..$LATEST" https://github.com/golang/text/compare/v$CURRENT..v$LATEST
golang.org/x/text v0.15.0
golang.org/x/text v0.16.0
// bump: gomod-gopkg.in/yaml.v3 /gopkg\.in\/yaml\.v3 v(.*)/ https://github.com/go-yaml/yaml.git|^3
// bump: gomod-gopkg.in/yaml.v3 command go get -d gopkg.in/yaml.v3@v$LATEST && go mod tidy
@ -79,6 +79,6 @@ require (
github.com/itchyny/timefmt-go v0.1.5 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/sys v0.21.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
)

24
go.sum
View File

@ -1,5 +1,5 @@
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA=
github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
github.com/ergochat/readline v0.1.1 h1:C8Uuo3ybB23GWOt0uxmHbGzKM9owmtXary6Clrj84s0=
@ -25,18 +25,18 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/wader/gojq v0.12.1-0.20240401131232-6c6bc364201a h1:P881Oecjt9FEXrwkGJ6UObJksxejJaF/fKq1ZfXpiVE=
github.com/wader/gojq v0.12.1-0.20240401131232-6c6bc364201a/go.mod h1:qVrzkUdnBtJvM4twyRQ6xdziPSnSp35dLm4s/DN2iP4=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=