├── .github ├── FUNDING.yml ├── renovate.json └── workflows │ ├── codetests.yml │ └── release.yml ├── .gitignore ├── .golangci.yml ├── pkg └── xt │ ├── filemode.go │ ├── job.go │ └── xt.go ├── LICENSE ├── go.mod ├── README.md ├── .goreleaser.yaml ├── main.go ├── MANUAL.md └── go.sum /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [Unpackerr] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /xt 2 | /xt.* 3 | /dist/ 4 | /MANUAL 5 | /MANUAL.html 6 | /MANUAL.gz -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | linters: 3 | default: all 4 | disable: 5 | - depguard 6 | - exhaustruct 7 | - nlreturn 8 | issues: 9 | max-issues-per-linter: 0 10 | max-same-issues: 0 11 | formatters: 12 | enable: 13 | - gci 14 | - gofmt 15 | - gofumpt 16 | - goimports 17 | -------------------------------------------------------------------------------- /.github/workflows/codetests.yml: -------------------------------------------------------------------------------- 1 | name: test-and-lint 2 | on: push 3 | permissions: 4 | contents: read 5 | jobs: 6 | gotest: 7 | strategy: 8 | matrix: 9 | os: [ubuntu, macos] 10 | runs-on: ${{ matrix.os }}-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions/setup-go@v5 14 | with: 15 | go-version: 'stable' 16 | - name: go-generate 17 | run: go generate ./... 18 | - name: go-test 19 | run: go test ./pkg/... 20 | - name: golangci-lint 21 | uses: golangci/golangci-lint-action@v7 22 | with: 23 | version: 'v2.1' 24 | -------------------------------------------------------------------------------- /pkg/xt/filemode.go: -------------------------------------------------------------------------------- 1 | package xt 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // FileMode is used to unmarshal a unix file mode from the config file. 11 | type FileMode os.FileMode //nolint:recvcheck // it works this way... 12 | 13 | // UnmarshalText turns a unix file mode, wrapped in quotes or not, into a usable os.FileMode. 14 | func (f *FileMode) UnmarshalText(text []byte) error { 15 | str := strings.TrimSpace(strings.Trim(string(text), `"'`)) 16 | 17 | fm, err := strconv.ParseUint(str, 8, 32) 18 | if err != nil { 19 | return fmt.Errorf("file_mode (%s) is invalid: %w", str, err) 20 | } 21 | 22 | *f = FileMode(os.FileMode(fm)) 23 | 24 | return nil 25 | } 26 | 27 | // MarshalText satisfies an encoder.TextMarshaler interface. 28 | func (f FileMode) MarshalText() ([]byte, error) { 29 | return []byte(f.String()), nil 30 | } 31 | 32 | // String creates a unix-octal version of a file mode. 33 | func (f FileMode) String() string { 34 | return fmt.Sprintf("%04o", f) 35 | } 36 | 37 | // Mode returns the compatible os.FileMode. 38 | func (f FileMode) Mode() os.FileMode { 39 | return os.FileMode(f) 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2025 David Newhall II 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Unpackerr/xt 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.2 6 | 7 | require ( 8 | github.com/spf13/pflag v1.0.6 9 | golift.io/version v0.0.2 10 | golift.io/xtractr v0.2.3-0.20250419170021-53bfe05970fe 11 | ) 12 | 13 | require ( 14 | github.com/cavaliergopher/cpio v1.0.1 // indirect 15 | github.com/cavaliergopher/rpm v1.3.0 // indirect 16 | github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect 17 | github.com/peterebden/ar v0.0.0-20241106141004-20dc11b778e8 // indirect 18 | github.com/sshaman1101/dcompress v0.0.0-20200109162717-50436a6332de // indirect 19 | github.com/therootcompany/xz v1.0.1 // indirect 20 | golang.org/x/crypto v0.37.0 // indirect 21 | ) 22 | 23 | require ( 24 | github.com/BurntSushi/toml v1.5.0 // indirect 25 | github.com/andybalholm/brotli v1.1.1 // indirect 26 | github.com/bodgit/plumbing v1.3.0 // indirect 27 | github.com/bodgit/sevenzip v1.6.0 // indirect 28 | github.com/bodgit/windows v1.0.1 // indirect 29 | github.com/hashicorp/errwrap v1.1.0 // indirect 30 | github.com/hashicorp/go-multierror v1.1.1 // indirect 31 | github.com/kdomanski/iso9660 v0.4.0 // indirect 32 | github.com/klauspost/compress v1.18.0 // indirect 33 | github.com/nwaples/rardecode v1.1.3 // indirect 34 | github.com/pierrec/lz4/v4 v4.1.22 // indirect 35 | github.com/ulikunitz/xz v0.5.12 // indirect 36 | go4.org v0.0.0-20230225012048-214862532bf5 // indirect 37 | golang.org/x/text v0.24.0 // indirect 38 | golift.io/cnfgfile v0.0.0-20240713024420-a5436d84eb48 39 | gopkg.in/yaml.v3 v3.0.1 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: build-and-release 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | 7 | permissions: 8 | contents: write 9 | packages: write 10 | 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Import GPG key 19 | id: import_gpg 20 | uses: crazy-max/ghaction-import-gpg@v6 21 | with: 22 | gpg_private_key: ${{ secrets.GPG_SIGNING_KEY }} 23 | - name: "Create GPG_SIGNING_KEY file" 24 | run: "echo '${{ secrets.GPG_SIGNING_KEY }}' > /tmp/key.gpg" 25 | - name: "Save Iteration in Revision" 26 | run: echo REVISION=$(git rev-list --count --all || echo 0) > $GITHUB_ENV 27 | - uses: actions/setup-go@v5 28 | with: 29 | go-version: stable 30 | cache: true 31 | - uses: goreleaser/goreleaser-action@v6 32 | with: 33 | version: nightly 34 | args: release --clean 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | # Use a fine grained PAT with Contents: R/W on golift/homebrew-mugs. 38 | HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} 39 | GPG_SIGNING_KEY: /tmp/key.gpg 40 | GPG_SIGNING_KEY_ID: ${{ steps.import_gpg.outputs.keyid }} 41 | - uses: golift/upload-packagecloud@v1 42 | with: 43 | userrepo: golift/pkgs 44 | apitoken: ${{ secrets.PACKAGECLOUD_TOKEN }} 45 | packages: dist/ 46 | rpmdists: el/6 47 | debdists: ubuntu/focal -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | eXtractor Tool 2 | 3 | Extract Everything. Decompress entire folders filled with archives. 4 | 5 | Simple command-line tool that works on mac, windows and linux. 6 | 7 | ## Installation 8 | 9 | Use the directions below to install `xt` on your system. 10 | 11 | ### Linux 12 | 13 | If you use **Alpine** or **Arch**, you can find a package for your OS on the [releases] page 14 | 15 | Running the following script will install the GoLift package repo (using packagecloud.io). 16 | This only works on Debian/Ubuntu and RedHat/Fedora Linux. 17 | This is the recommend installation method for all users. 18 | 19 | ```shell 20 | curl -s https://golift.io/repo.sh | sudo bash -s - xt 21 | ``` 22 | 23 | ### macOS 24 | 25 | - There's a binary on the [releases] page, but you should use Homebrew: 26 | 27 | ```shell 28 | brew install golift/mugs/xt 29 | ``` 30 | 31 | ### Windows 32 | 33 | - Download an `exe` from the [releases] page and put it in your `PATH` somewhere. 34 | - The tool only works in a command or terminal window. 35 | 36 | ### FreeBSD 37 | 38 | - Download a FreeBSD binary from the [releases] page and extract it into your PATH somewhere. 39 | - Recommend `/usr/local/bin`. 40 | - _Maybe one day [GoReleaser] will support FreeBSD packages._ 41 | 42 | ### Others 43 | 44 | - This should work on any operating system that supports GoLang. 45 | - After you [install go](https://go.dev/doc/install), install the `xt` app using `go install`: 46 | 47 | ```shell 48 | cd /tmp 49 | go get github.com/Unpackerr/xt 50 | go install github.com/Unpackerr/xt 51 | ``` 52 | 53 | [releases]: https://github.com/Unpackerr/xt/releases 54 | [GoReleaser]: https://github.com/goreleaser/goreleaser 55 | -------------------------------------------------------------------------------- /pkg/xt/job.go: -------------------------------------------------------------------------------- 1 | package xt 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "golift.io/cnfgfile" 8 | ) 9 | 10 | // Job defines the input data for one extraction run. 11 | type Job struct { 12 | Paths []string `json:"paths" toml:"paths" xml:"path" yaml:"paths"` 13 | Output string `json:"output" toml:"output" xml:"output" yaml:"output"` 14 | Passwords []string `json:"passwords" toml:"passwords" xml:"password" yaml:"passwords"` 15 | Exclude []string `json:"excludeSuffix" toml:"exclude_suffix" xml:"exclude_suffix" yaml:"excludeSuffix"` 16 | Include []string `json:"includeSuffix" toml:"include_suffix" xml:"include_suffix" yaml:"includeSuffix"` 17 | MaxDepth uint16 `json:"maxDepth" toml:"max_depth" xml:"max_depth" yaml:"maxDepth"` 18 | MinDepth uint16 `json:"minDepth" toml:"min_depth" xml:"min_depth" yaml:"minDepth"` 19 | DirMode FileMode `json:"dirMode" toml:"dir_mode" xml:"dir_mode" yaml:"dirMode"` 20 | FileMode FileMode `json:"fileMode" toml:"file_mode" xml:"file_mode" yaml:"fileMode"` 21 | SquashRoot bool `json:"squashRoot" toml:"squash_root" xml:"squash_root" yaml:"squashRoot"` 22 | DebugLog bool `json:"debugLog" toml:"debug_log" xml:"debug_log" yaml:"debugLog"` 23 | Preserve bool `json:"preservePaths" toml:"preserve_paths" xml:"preserve_paths" yaml:"preservePaths"` 24 | Verbose bool `json:"verbose" toml:"verbose" xml:"verbose" yaml:"verbose"` 25 | } 26 | 27 | // ParseJobs checks for and reads more jobs in from 0 or more job files. 28 | func ParseJobs(jobFiles []string) ([]*Job, error) { 29 | jobs := make([]*Job, len(jobFiles)) 30 | 31 | for idx, jobFile := range jobFiles { 32 | jobs[idx] = &Job{} 33 | // This library simply parses xml, json, toml, and yaml into a data struct. 34 | // It parses based on file name extension, toml is default. Supports compression. 35 | err := cnfgfile.Unmarshal(jobs[idx], jobFile) 36 | if err != nil { 37 | return nil, fmt.Errorf("bad job file: %w", err) 38 | } 39 | } 40 | 41 | return jobs, nil 42 | } 43 | 44 | func (j *Job) String() string { 45 | j.fixModes() 46 | 47 | sSfx := "" 48 | if len(j.Paths) > 1 { 49 | sSfx = "s" 50 | } 51 | 52 | return fmt.Sprintf("%d path%s, f/d-mode:%s/%s, min/max-depth: %d/%d, preserve/squash: %v/%v output: %s", 53 | len(j.Paths), sSfx, j.FileMode, j.DirMode, j.MinDepth, j.MaxDepth, j.Preserve, j.SquashRoot, j.Output) 54 | } 55 | 56 | // Debugf prints a log message if debug is enabled. 57 | func (j *Job) Debugf(format string, vars ...any) { 58 | if j.DebugLog { 59 | log.Printf("[DEBUG] "+format, vars...) 60 | } 61 | } 62 | 63 | // Printf wraps log.Printf. 64 | func (j *Job) Printf(format string, vars ...any) { 65 | log.Printf(format, vars...) 66 | } 67 | 68 | func (j *Job) fixModes() { 69 | const ( 70 | defaultFileMode = 0o644 71 | defaultDirMode = 0o755 72 | ) 73 | 74 | if j.DirMode == 0 { 75 | j.DirMode = defaultDirMode 76 | } 77 | 78 | if j.FileMode == 0 { 79 | j.FileMode = defaultFileMode 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | before: 4 | hooks: 5 | - go mod tidy 6 | - go generate ./... 7 | ## This creates MANUAL and MANUAL.html. The gzip then makes MANUAL.gz (a man page). 8 | - go run github.com/davidnewhall/md2roff@v0.0.1 --manual xt --version "{{.Version}}-{{.Env.REVISION}}" --date "{{.Date}}" MANUAL.md 9 | - rm -f MANUAL.gz 10 | - gzip -9 MANUAL 11 | 12 | builds: 13 | - env: 14 | - CGO_ENABLED=0 15 | - CGO_LDFLAGS=-mmacosx-version-min=10.8 16 | - CGO_CFLAGS=-mmacosx-version-min=10.8 17 | goos: 18 | - linux 19 | - windows 20 | - freebsd 21 | - darwin 22 | goarch: 23 | - amd64 24 | - arm 25 | - arm64 26 | - '386' 27 | ldflags: 28 | - -s -w 29 | - -X "golift.io/version.Version={{.Version}}" 30 | - -X "golift.io/version.BuildDate={{.Date}}" 31 | - -X "golift.io/version.BuildUser={{.Env.USER}}" 32 | - -X "golift.io/version.Revision={{.Env.REVISION}}" 33 | - -X "golift.io/version.Branch={{.ShortCommit}} [{{.Branch}}]" 34 | ignore: 35 | - goos: windows 36 | goarch: arm 37 | - goos: darwin 38 | goarch: arm 39 | - goos: darwin 40 | goarch: '386' 41 | 42 | universal_binaries: 43 | - replace: true 44 | 45 | archives: 46 | - formats: ['tar.gz'] 47 | wrap_in_directory: true 48 | files: 49 | - src: MANUAL.html 50 | dst: xt-manual.html 51 | - src: LICENSE 52 | dst: LICENSE.txt 53 | format_overrides: 54 | - goos: windows 55 | formats: ['zip'] 56 | 57 | nfpms: 58 | - id: xt-packages 59 | vendor: Go Lift 60 | homepage: https://unpackerr.com 61 | maintainer: David Newhall II 62 | description: eXtractor Tool - Recursively decompress archives 63 | license: MIT 64 | formats: 65 | - deb 66 | - rpm 67 | - archlinux 68 | - apk 69 | bindir: /usr/bin 70 | version_metadata: git 71 | section: default 72 | priority: extra 73 | provides: 74 | - xt 75 | rpm: 76 | signature: 77 | key_file: "{{ .Env.GPG_SIGNING_KEY }}" 78 | deb: 79 | signature: 80 | key_file: "{{ .Env.GPG_SIGNING_KEY }}" 81 | type: origin 82 | contents: 83 | - src: MANUAL.gz 84 | dst: /usr/share/man/man1/xt.1.gz 85 | - src: LICENSE 86 | dst: /usr/share/doc/xt/LICENSE.txt 87 | - src: MANUAL.md 88 | dst: /usr/share/doc/xt/MANUAL.md 89 | 90 | signs: 91 | - artifacts: all 92 | 93 | brews: 94 | - name: xt 95 | # enable the line below only for testing locally 96 | #skip_upload: true 97 | repository: 98 | owner: golift 99 | name: homebrew-mugs 100 | branch: master 101 | token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" 102 | commit_author: 103 | name: goreleaserbot 104 | email: bot@goreleaser.com 105 | commit_msg_template: "Brew formula update for {{ .ProjectName }} version {{ .Tag }}" 106 | directory: Formula 107 | homepage: https://unpackerr.com/ 108 | description: "eXtractor Tool - Recursively decompress archives" 109 | license: MIT 110 | url_template: "https://github.com/Unpackerr/xt/releases/download/{{ .Tag }}/{{ .ArtifactName }}" 111 | test: assert_match "xt v#{version}", shell_output("#{bin}/xt -v 2>&1", 2) 112 | install: bin.install "xt" 113 | 114 | changelog: 115 | sort: asc 116 | filters: 117 | exclude: 118 | - "^docs:" 119 | - "^test:" 120 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | //nolint:forbidigo 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/Unpackerr/xt/pkg/xt" 10 | flag "github.com/spf13/pflag" 11 | "golift.io/version" 12 | "golift.io/xtractr" 13 | ) 14 | 15 | func parseFlags(pwd string) (*xt.Job, *flags) { 16 | flag.Usage = func() { 17 | // XXX: Write more "help" info here. 18 | fmt.Println("If you pass a directory, this app will extract every archive in it.") 19 | fmt.Printf("Usage: %s [-v] [--output ] [paths...]\n", os.Args[0]) 20 | flag.PrintDefaults() 21 | os.Exit(0) 22 | } 23 | job := &xt.Job{} 24 | flags := &flags{} 25 | 26 | flag.BoolVarP(&flags.PrintVer, "version", "v", false, "Print application version, and supported extensions.") 27 | // These cli options create 1 job. Using job files creates N jobs. 28 | flag.StringVarP(&job.Output, "output", "o", pwd, "Output directory, default is current directory.") 29 | flag.Uint16VarP(&job.MaxDepth, "max-depth", "d", 0, "Maximum folder depth to recursively search for archives.") 30 | flag.Uint16VarP(&job.MinDepth, "min-depth", "m", 0, "Minimum folder depth to recursively search for archives.") 31 | flag.StringSliceVarP(&job.Include, "extension", "e", nil, "Only extract files with these extensions.") 32 | flag.StringSliceVarP(&job.Passwords, "password", "P", nil, "Attempt these passwords for rar and 7zip archives.") 33 | flag.BoolVarP(&job.SquashRoot, "squash-root", "S", false, 34 | "If archive contains only 1 folder at in the root, its contents are moved into output folder.") 35 | flag.BoolVarP(&job.Verbose, "verbose", "V", false, "Verbose output prints the file list that was extracted.") 36 | flag.BoolVarP(&job.DebugLog, "debug", "D", false, "Enable debug output.") 37 | flag.StringSliceVarP(&flags.JobFiles, "job-file", "j", nil, "Read additional extraction jobs from these files.") 38 | flag.BoolVarP(&job.Preserve, "preserve-paths", "p", false, "Recreate directory hierarchy while extracting.") 39 | flag.Parse() 40 | // flag.UintVarP(&job.Recurse, "recurse", "r", 0, "Extract archives inside archives, up to this depth.") 41 | 42 | job.Paths = flag.Args() 43 | 44 | return job, flags 45 | } 46 | 47 | // flags contains the non-job flags used on the cli. 48 | type flags struct { 49 | PrintVer bool 50 | JobFiles []string 51 | } 52 | 53 | func main() { 54 | // Where we extract to. 55 | pwd, err := os.Getwd() 56 | if err != nil { 57 | pwd = "." 58 | } 59 | 60 | // Get 1 job and other flag info from cli args. 61 | cliJob, flags := parseFlags(pwd) 62 | flags.printVer() 63 | 64 | // Read in jobs from 1 or more job files. 65 | jobs, err := xt.ParseJobs(flags.JobFiles) 66 | if err != nil { 67 | log.Fatal("[ERROR]", err) 68 | } 69 | 70 | // Append cli job to job file jobs. 71 | if len(cliJob.Paths) > 0 { 72 | jobs = append(jobs, cliJob) 73 | } 74 | 75 | // Check for jobs? 76 | if len(jobs) < 1 || len(jobs[0].Paths) < 1 { 77 | flag.Usage() 78 | } 79 | 80 | // Extract the jobs. 81 | for i, job := range jobs { 82 | log.Printf("Starting Job %d of %d with %s", i+1, len(jobs), job) 83 | xt.Extract(job) 84 | } 85 | } 86 | 87 | // printVer prints the version and exits. 88 | // Only if the user passed -v or --version. 89 | func (f *flags) printVer() { 90 | if !f.PrintVer { 91 | return 92 | } 93 | 94 | fmt.Printf("xt v%s-%s (%s)\n", version.Version, version.Revision, version.Branch) 95 | fmt.Println(" - Supported Extensions:") 96 | 97 | for _, ext := range xtractr.SupportedExtensions() { 98 | fmt.Println(" ", ext) 99 | } 100 | 101 | os.Exit(0) 102 | } 103 | -------------------------------------------------------------------------------- /MANUAL.md: -------------------------------------------------------------------------------- 1 | xt(1) -- eXtractor Tool - Recursively decompress archives 2 | === 3 | 4 | SYNOPSIS 5 | --- 6 | 7 | `xt [options] [path [path] [path] ...]` 8 | `xt --job-file /tmp/job1 -j /tmp/job2`\ 9 | 10 | DESCRIPTION 11 | --- 12 | 13 | * This application recursively extracts compressed archives. 14 | * Provide directories to search or provide files to extract. 15 | * Supports: ZIP, RAR, GZIP, BZIP2, TAR, TGZ, TBZ2, 7ZIP, ISO9660 16 | * Supports: Z, AR, BR, CPIO, DEB, LZ/4, LZIP, LZMA2, S2, SNAPPY 17 | * Supports: RPM, SZ, TLZ, TXZ, ZLIB, ZSTD, BROTLI, ZZ 18 | * ie: *.zip *.rar *.r00 *.gz *.bz2 *.tar *.tgz *.tbz2 *.7z *.iso (and others) 19 | 20 | OPTIONS 21 | --- 22 | 23 | `xt [-o ] [-d <#>] [-m <#>] [-e <.ext>] [-P ] [-p] [paths]` 24 | 25 | -o _directory_, --output _directory_ 26 | Provide a file system _directory_ where content is written. 27 | The default output _directory_ is the current working directory. 28 | 29 | -S, --squash-root 30 | If an archive contains only a single folder in the root directory, 31 | then the contents of that folder are moved into the output folder. 32 | The now-empty original root folder is deleted. 33 | 34 | -d _count_, --max-depth _count_ 35 | This option limits how deep into the file system xt recurses. 36 | The default is (0) unlimited. Setting to 1 disables recursion. 37 | 38 | -m _count_, --min-depth _count_ 39 | This option determines if archives should only be found deeper 40 | into the file system. The default is (0) root. Archives are only 41 | extracted from _count_ sub directories deep or deeper. 42 | 43 | -P _password_, --password _password_ 44 | Provided _passwords_ are attempted against extraction of encrypted 45 | rar and/or 7zip archives. The `-p` option may be provided many times. 46 | 47 | -e _.ext_, --extension _.ext_ 48 | Only extract archives with these extensions. Include the leading dot. 49 | The `-e` option may be provided many times. 50 | Use `-v` for supported extensions. <- Your input must match the 51 | supported extensions. Unknown extensions are still ignored. 52 | 53 | -j _file_, --job-file _file_ 54 | The options above create a single job. If you want more control, 55 | you may provide one or more job files. Each _file_ may define the 56 | input, output, depths and passwords, etc. Acceptable formats are 57 | xml, json, toml and yaml. TOML is the default. See JOB FILES below. 58 | 59 | -p, --preserve-paths 60 | This option determines if the archives will be extracted to their 61 | parent folder. Using this flag will override the --output option. 62 | 63 | -V, --verbose 64 | Verbose logging prints the extracted file paths. 65 | 66 | -D, --debug 67 | Enable debug output. 68 | 69 | -v, --version 70 | Display version and exit. 71 | 72 | -h, --help 73 | Display usage and exit. 74 | 75 | JOB FILES 76 | --- 77 | 78 | If `include_suffix` is provided `exclude_suffix` is ignored. 79 | 80 | Example TOML job file: 81 | 82 | paths = [ '/path1', '/another/path' ] 83 | output = '.' 84 | passwords = [ 'password1', '''password"With'Specials!''', 'pass3'] 85 | exclude_suffix = ['.iso', '.gz'] 86 | include_suffix = ['.zip', '.rar', '.r00'] 87 | max_depth = 0 88 | min_depth = 1 89 | file_mode = 644 90 | dir_mode = 755 91 | verbose = false 92 | debug = false 93 | preserve_paths = false 94 | 95 | AUTHOR 96 | --- 97 | 98 | * David Newhall II - 1/20/2024 99 | 100 | LOCATION 101 | --- 102 | 103 | * https://unpackerr.zip/xt 104 | * https://golift.io/gpg 105 | * /usr/bin/xt || /usr/local/bin/xt 106 | -------------------------------------------------------------------------------- /pkg/xt/xt.go: -------------------------------------------------------------------------------- 1 | // Package xt provides the interface to extract archive files based on job parameters. 2 | package xt 3 | 4 | import ( 5 | "fmt" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "time" 11 | 12 | "golift.io/xtractr" 13 | ) 14 | 15 | // Extract runs a job immediately. 16 | func Extract(job *Job) { 17 | archives := job.getArchives() 18 | if len(archives) == 0 { 19 | log.Println("==> No archives found in:", job.Paths) 20 | } 21 | 22 | job.fixModes() 23 | 24 | total := archives.Count() 25 | count := 0 26 | size := int64(0) 27 | fCount := 0 28 | start := time.Now() 29 | 30 | for folder, files := range archives { 31 | for _, archive := range files { 32 | count++ 33 | log.Printf("==> Extracting Archive (%d/%d): %s", count, total, archive) 34 | 35 | output, fSize, files, duration, err := job.processArchive(folder, archive) 36 | if err != nil { 37 | log.Printf("[ERROR] Extracting: %v", err) 38 | } else { 39 | log.Printf("==> Extracted Archive %s to %s in %v: bytes: %d, files: %d", 40 | archive, output, duration.Round(time.Millisecond), fSize, len(files)) 41 | } 42 | 43 | if len(files) > 0 && job.Verbose { 44 | log.Printf("==> Files:\n - %s", strings.Join(files, "\n - ")) 45 | } 46 | 47 | size += fSize 48 | fCount += len(files) 49 | } 50 | } 51 | 52 | log.Printf("==> Done.") 53 | log.Printf("==> Extracted %d archives; wrote %d bytes into %d files in %v", 54 | total, size, fCount, time.Since(start).Round(time.Millisecond)) 55 | } 56 | 57 | func (j *Job) processArchive(folder, archive string) (string, int64, []string, time.Duration, error) { 58 | file := &xtractr.XFile{ 59 | FilePath: archive, // Path to archive being extracted. 60 | OutputDir: j.Output, // Folder to extract archive into. 61 | FileMode: j.FileMode.Mode(), // Write files with this mode. 62 | DirMode: j.DirMode.Mode(), // Write folders with this mode. 63 | Passwords: j.Passwords, // (RAR/7zip) Archive password(s). 64 | SquashRoot: j.SquashRoot, // Remove single root folder? 65 | } 66 | file.SetLogger(j) 67 | 68 | // If preserving the file hierarchy: set the output directory to the same path as the input file. 69 | if j.Preserve { 70 | // Remove input path prefix from fileName, 71 | // append fileName.Dir to job.Output, 72 | // extract file into job.Output/file(sub)Folder(s). 73 | file.OutputDir = filepath.Join(j.Output, filepath.Dir(strings.TrimPrefix(archive, folder))) 74 | } 75 | 76 | start := time.Now() 77 | 78 | size, files, _, err := xtractr.ExtractFile(file) 79 | if err != nil { 80 | err = fmt.Errorf("archive: %s: %w", archive, err) 81 | } 82 | 83 | return file.OutputDir, size, files, time.Since(start), err 84 | } 85 | 86 | func (j *Job) getArchives() xtractr.ArchiveList { 87 | archives := map[string][]string{} 88 | 89 | for _, fileName := range j.Paths { 90 | fileInfo, err := os.Stat(fileName) 91 | if err != nil { 92 | log.Println("[ERROR] Reading archive path:", err) 93 | continue 94 | } 95 | 96 | if !fileInfo.IsDir() { 97 | archives[fileName] = []string{fileName} 98 | continue 99 | } 100 | 101 | exclude := j.Exclude 102 | if len(j.Include) > 0 { 103 | exclude = xtractr.AllExcept(j.Include...) 104 | } 105 | 106 | for _, fileList := range xtractr.FindCompressedFiles(xtractr.Filter{ 107 | Path: fileName, 108 | ExcludeSuffix: exclude, 109 | MaxDepth: int(j.MaxDepth), 110 | MinDepth: int(j.MinDepth), 111 | }) { 112 | // Group archive lists by the parent search folder that found them. 113 | archives[fileName] = append(archives[fileName], fileList...) 114 | } 115 | } 116 | 117 | return archives 118 | } 119 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 10 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 11 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 12 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 13 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 14 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 15 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 16 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 17 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 18 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 19 | github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= 20 | github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= 21 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 22 | github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= 23 | github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= 24 | github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= 25 | github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= 26 | github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A= 27 | github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc= 28 | github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= 29 | github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= 30 | github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= 31 | github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= 32 | github.com/cavaliergopher/rpm v1.3.0 h1:UHX46sasX8MesUXXQ+UbkFLUX4eUWTlEcX8jcnRBIgI= 33 | github.com/cavaliergopher/rpm v1.3.0/go.mod h1:vEumo1vvtrHM1Ov86f6+k8j7zNKOxQfHDCAIcR/36ZI= 34 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 35 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 36 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 37 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 38 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 39 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 40 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 41 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 42 | github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= 43 | github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= 44 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 45 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 46 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 47 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 48 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 49 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 50 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 51 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 52 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 53 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 54 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 55 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 56 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 57 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 58 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 59 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 60 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 61 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 62 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 63 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 64 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 65 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 66 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 67 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 68 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 69 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 70 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 71 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 72 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 73 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 74 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 75 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 76 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 77 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 78 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 79 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 80 | github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= 81 | github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 82 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 83 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 84 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 85 | github.com/kdomanski/iso9660 v0.4.0 h1:BPKKdcINz3m0MdjIMwS0wx1nofsOjxOq8TOr45WGHFg= 86 | github.com/kdomanski/iso9660 v0.4.0/go.mod h1:OxUSupHsO9ceI8lBLPJKWBTphLemjrCQY8LPXM7qSzU= 87 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 88 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 89 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 90 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 91 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 92 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 93 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 94 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 95 | github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= 96 | github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 97 | github.com/peterebden/ar v0.0.0-20241106141004-20dc11b778e8 h1:27L3dHkYbeWGU3/5NasAzVDgXG9QzlfKCvcl4cdNW6c= 98 | github.com/peterebden/ar v0.0.0-20241106141004-20dc11b778e8/go.mod h1:hpFkyhCgB5Rm8FK+ISypOE+9UyrCuL6MNcjPMB1s1ec= 99 | github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= 100 | github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 101 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 102 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 103 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 104 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 105 | github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= 106 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 107 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 108 | github.com/sshaman1101/dcompress v0.0.0-20200109162717-50436a6332de h1:uIeuAon/xwRdiZaCmEd5mocquesYkWCf71WBO7obTmA= 109 | github.com/sshaman1101/dcompress v0.0.0-20200109162717-50436a6332de/go.mod h1:XIUpD+1rteMazWrMFjNSpM6TocSHxDYXk6UEgBb5+F0= 110 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 111 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 112 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 113 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 114 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 115 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 116 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 117 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 118 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 119 | github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= 120 | github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= 121 | github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= 122 | github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 123 | github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= 124 | github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= 125 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 126 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 127 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 128 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 129 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 130 | go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= 131 | go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= 132 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 133 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 134 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 135 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 136 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 137 | golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= 138 | golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= 139 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 140 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 141 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 142 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 143 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 144 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 145 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 146 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 147 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 148 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 149 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 150 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 151 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 152 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 153 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 154 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 155 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 156 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 157 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 158 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 159 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 160 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 161 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 162 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 163 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 164 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 165 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 166 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 167 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 168 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 169 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 170 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 171 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 172 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 173 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 174 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 175 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 176 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 177 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 178 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 179 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 180 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 181 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 182 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 183 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 184 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 185 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 186 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 187 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 188 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 189 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 190 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 191 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 192 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 193 | golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= 194 | golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 195 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 196 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 197 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 198 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 199 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 200 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 201 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 202 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 203 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 204 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 205 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 206 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 207 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 208 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 209 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 210 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 211 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 212 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 213 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 214 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 215 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 216 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 217 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 218 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 219 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 220 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 221 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 222 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= 223 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= 224 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 225 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 226 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 227 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 228 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 229 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 230 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 231 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 232 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 233 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 234 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 235 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 236 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 237 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 238 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 239 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 240 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 241 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 242 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 243 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 244 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 245 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 246 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 247 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 248 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 249 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 250 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 251 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 252 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 253 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 254 | golift.io/cnfgfile v0.0.0-20240713024420-a5436d84eb48 h1:c7cJWRr0cUnFHKtq072esKzhQHKlFA5YRY/hPzQrdko= 255 | golift.io/cnfgfile v0.0.0-20240713024420-a5436d84eb48/go.mod h1:zHm9o8SkZ6Mm5DfGahsrEJPsogyR0qItP59s5lJ98/I= 256 | golift.io/version v0.0.2 h1:i0gXRuSDHKs4O0sVDUg4+vNIuOxYoXhaxspftu2FRTE= 257 | golift.io/version v0.0.2/go.mod h1:76aHNz8/Pm7CbuxIsDi97jABL5Zui3f2uZxDm4vB6hU= 258 | golift.io/xtractr v0.2.3-0.20250419170021-53bfe05970fe h1:fCqAf/BYLNy5BdX6IeeGIGutHqOANjIfKeYANd5Cktg= 259 | golift.io/xtractr v0.2.3-0.20250419170021-53bfe05970fe/go.mod h1:invEOYfyBnFtegY2V2n+9K5bUEHB8pGZng1BK0U2r38= 260 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 261 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 262 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 263 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 264 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 265 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 266 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 267 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 268 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 269 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 270 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 271 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 272 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 273 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 274 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 275 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 276 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 277 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 278 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 279 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 280 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 281 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 282 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 283 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 284 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 285 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 286 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 287 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 288 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 289 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 290 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 291 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 292 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 293 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 294 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 295 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 296 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 297 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 298 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 299 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 300 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 301 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 302 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 303 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 304 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 305 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 306 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 307 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 308 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 309 | --------------------------------------------------------------------------------