diff --git a/.github/workflows/codetests.yml b/.github/workflows/codetests.yml index ca18b38..b183f33 100644 --- a/.github/workflows/codetests.yml +++ b/.github/workflows/codetests.yml @@ -18,6 +18,6 @@ jobs: - name: go-test run: go test ./pkg/... - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v7 with: - version: 'v1.55' + version: 'v2.1' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d409861..8273d79 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: with: go-version: stable cache: true - - uses: goreleaser/goreleaser-action@v5 + - uses: goreleaser/goreleaser-action@v6 with: version: nightly args: release --clean diff --git a/.golangci.yml b/.golangci.yml index 12324ba..4afda96 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,37 +1,16 @@ -run: - timeout: 3m - -output: - sort-results: true - +version: "2" linters: - enable-all: true + default: all disable: - # deprecated - - maligned - - scopelint - - interfacer - - golint - - exhaustivestruct - - nosnakecase - - structcheck - - deadcode - - varcheck - - ifshort - # unused - - nlreturn - - exhaustruct - depguard - - tagalign - + - exhaustruct + - nlreturn issues: - # disable the default limit so we see everything - max-same-issues: 0 max-issues-per-linter: 0 - exclude-rules: - # Exclude some linters from testing files. - - linters: - - goconst - - wsl - - funlen - path: '.+_test.go' \ No newline at end of file + max-same-issues: 0 +formatters: + enable: + - gci + - gofmt + - gofumpt + - goimports diff --git a/.goreleaser.yaml b/.goreleaser.yaml index c645a78..47d52a4 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -5,7 +5,7 @@ before: - go mod tidy - go generate ./... ## This creates MANUAL and MANUAL.html. The gzip then makes MANUAL.gz (a man page). - - go run github.com/davidnewhall/md2roff@v0.0.1 --manual xt --version "{{.Version}}" --date "{{.Date}}" MANUAL.md + - go run github.com/davidnewhall/md2roff@v0.0.1 --manual xt --version "{{.Version}}-{{.Env.REVISION}}" --date "{{.Date}}" MANUAL.md - rm -f MANUAL.gz - gzip -9 MANUAL @@ -30,7 +30,7 @@ builds: - -X "golift.io/version.BuildDate={{.Date}}" - -X "golift.io/version.BuildUser={{.Env.USER}}" - -X "golift.io/version.Revision={{.Env.REVISION}}" - - -X "golift.io/version.Branch={{.Branch}} ({{.ShortCommit}})" + - -X "golift.io/version.Branch={{.ShortCommit}} [{{.Branch}}]" ignore: - goos: windows goarch: arm diff --git a/LICENSE b/LICENSE index 42f0c54..5ee9c52 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023-2024 David Newhall II +Copyright (c) 2023-2025 David Newhall II Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MANUAL.md b/MANUAL.md index 1962e2b..5104566 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -24,6 +24,11 @@ OPTIONS Provide a file system _directory_ where content is written. The default output _directory_ is the current working directory. +-S, --squash-root + If an archive contains only a single folder in the root directory, + then the contents of that folder are moved into the output folder. + The now-empty original root folder is deleted. + -d _count_, --max-depth _count_ This option limits how deep into the file system xt recurses. The default is (0) unlimited. Setting to 1 disables recursion. @@ -49,6 +54,9 @@ OPTIONS input, output, depths and passwords, etc. Acceptable formats are xml, json, toml and yaml. TOML is the default. See JOB FILES below. +-D, --debug + Enable debug output. + -v, --version Display version and exit. diff --git a/go.mod b/go.mod index 47da916..0a19476 100644 --- a/go.mod +++ b/go.mod @@ -1,37 +1,40 @@ module github.com/Unpackerr/xt -go 1.20 +go 1.23.0 + +toolchain go1.24.2 require ( - github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace + github.com/spf13/pflag v1.0.6 golift.io/version v0.0.2 - golift.io/xtractr v0.2.3-0.20240202194230-2edf9aff9619 + golift.io/xtractr v0.2.3-0.20250419070747-b391d40d7453 ) require ( github.com/cavaliergopher/cpio v1.0.1 // indirect - github.com/cavaliergopher/rpm v1.2.0 // indirect - github.com/peterebden/ar v0.0.0-20230524111245-4f7c7b065694 // indirect + github.com/cavaliergopher/rpm v1.3.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/peterebden/ar v0.0.0-20241106141004-20dc11b778e8 // indirect github.com/sshaman1101/dcompress v0.0.0-20200109162717-50436a6332de // indirect github.com/therootcompany/xz v1.0.1 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/crypto v0.37.0 // indirect ) require ( - github.com/BurntSushi/toml v1.3.0 // indirect - github.com/andybalholm/brotli v1.1.0 // indirect + github.com/BurntSushi/toml v1.5.0 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect github.com/bodgit/plumbing v1.3.0 // indirect - github.com/bodgit/sevenzip v1.4.5 // indirect + github.com/bodgit/sevenzip v1.6.0 // indirect github.com/bodgit/windows v1.0.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/kdomanski/iso9660 v0.4.0 // indirect - github.com/klauspost/compress v1.17.5 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/nwaples/rardecode v1.1.3 // indirect - github.com/pierrec/lz4/v4 v4.1.21 // indirect - github.com/ulikunitz/xz v0.5.11 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/ulikunitz/xz v0.5.12 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/text v0.14.0 // indirect - golift.io/cnfgfile v0.0.0-20230531075023-f880041cc0a0 + golang.org/x/text v0.24.0 // indirect + golift.io/cnfgfile v0.0.0-20240713024420-a5436d84eb48 gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d8ec527..8d13a28 100644 --- a/go.sum +++ b/go.sum @@ -16,21 +16,21 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.0 h1:Ws8e5YmnrGEHzZEzg0YvK/7COGYtTC5PbaH9oSSbgfA= -github.com/BurntSushi/toml v1.3.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= -github.com/bodgit/sevenzip v1.4.5 h1:HFJQ+nbjppfyf2xbQEJBbmVo+o2kTg1FXV4i7YOx87s= -github.com/bodgit/sevenzip v1.4.5/go.mod h1:LAcAg/UQzyjzCQSGBPZFYzoiHMfT6Gk+3tMSjUk3foY= +github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A= +github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc= github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= -github.com/cavaliergopher/rpm v1.2.0 h1:s0h+QeVK252QFTolkhGiMeQ1f+tMeIMhGl8B1HUmGUc= -github.com/cavaliergopher/rpm v1.2.0/go.mod h1:R0q3vTqa7RUvPofAZYrnjJ63hh2vngjFfphuXiExVos= +github.com/cavaliergopher/rpm v1.3.0 h1:UHX46sasX8MesUXXQ+UbkFLUX4eUWTlEcX8jcnRBIgI= +github.com/cavaliergopher/rpm v1.3.0/go.mod h1:vEumo1vvtrHM1Ov86f6+k8j7zNKOxQfHDCAIcR/36ZI= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -39,6 +39,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= +github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -75,14 +77,16 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kdomanski/iso9660 v0.4.0 h1:BPKKdcINz3m0MdjIMwS0wx1nofsOjxOq8TOr45WGHFg= github.com/kdomanski/iso9660 v0.4.0/go.mod h1:OxUSupHsO9ceI8lBLPJKWBTphLemjrCQY8LPXM7qSzU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.5 h1:d4vBd+7CHydUqpFBgUEKkSdtSugf9YFmSkvUYPquI5E= -github.com/klauspost/compress v1.17.5/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -90,17 +94,17 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/peterebden/ar v0.0.0-20230524111245-4f7c7b065694 h1:pDBk3JWSIjS3gNxwEk1RjGdyZLsyTW4pOHaShBs9FK8= -github.com/peterebden/ar v0.0.0-20230524111245-4f7c7b065694/go.mod h1:hpFkyhCgB5Rm8FK+ISypOE+9UyrCuL6MNcjPMB1s1ec= -github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/peterebden/ar v0.0.0-20241106141004-20dc11b778e8 h1:27L3dHkYbeWGU3/5NasAzVDgXG9QzlfKCvcl4cdNW6c= +github.com/peterebden/ar v0.0.0-20241106141004-20dc11b778e8/go.mod h1:hpFkyhCgB5Rm8FK+ISypOE+9UyrCuL6MNcjPMB1s1ec= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA= -github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/sshaman1101/dcompress v0.0.0-20200109162717-50436a6332de h1:uIeuAon/xwRdiZaCmEd5mocquesYkWCf71WBO7obTmA= github.com/sshaman1101/dcompress v0.0.0-20200109162717-50436a6332de/go.mod h1:XIUpD+1rteMazWrMFjNSpM6TocSHxDYXk6UEgBb5+F0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -110,11 +114,14 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= -github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= -github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= +github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -126,8 +133,9 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -182,6 +190,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -209,8 +219,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -241,13 +251,12 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golift.io/cnfg v0.2.2 h1:m60kGVZQLuzAx939fLX8wSEKk2fpZdX6+HOjdYuUfCw= -golift.io/cnfgfile v0.0.0-20230531075023-f880041cc0a0 h1:u14MTHfTGx7kLX2b3wFbaESrIm3YqL//HM92XA6wzH4= -golift.io/cnfgfile v0.0.0-20230531075023-f880041cc0a0/go.mod h1:dVC3N+72/nHNlBv3cwXb4FQTxMAor+JlNClAldwACkY= +golift.io/cnfgfile v0.0.0-20240713024420-a5436d84eb48 h1:c7cJWRr0cUnFHKtq072esKzhQHKlFA5YRY/hPzQrdko= +golift.io/cnfgfile v0.0.0-20240713024420-a5436d84eb48/go.mod h1:zHm9o8SkZ6Mm5DfGahsrEJPsogyR0qItP59s5lJ98/I= golift.io/version v0.0.2 h1:i0gXRuSDHKs4O0sVDUg4+vNIuOxYoXhaxspftu2FRTE= golift.io/version v0.0.2/go.mod h1:76aHNz8/Pm7CbuxIsDi97jABL5Zui3f2uZxDm4vB6hU= -golift.io/xtractr v0.2.3-0.20240202194230-2edf9aff9619 h1:rLCt2Q6zTh3yvs3mw0tujq7FiYq+Q1lc6DDNSV5U7lk= -golift.io/xtractr v0.2.3-0.20240202194230-2edf9aff9619/go.mod h1:B6NSeohycJeS5pur983rw8JUDptMfGldIjqXXtJ65Po= +golift.io/xtractr v0.2.3-0.20250419070747-b391d40d7453 h1:JnozjdGe8GNAgcLDxd6WjWxVpktsmqJzP2PMT+DulhE= +golift.io/xtractr v0.2.3-0.20250419070747-b391d40d7453/go.mod h1:invEOYfyBnFtegY2V2n+9K5bUEHB8pGZng1BK0U2r38= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/main.go b/main.go index 3c3011a..0593f25 100644 --- a/main.go +++ b/main.go @@ -23,13 +23,16 @@ func parseFlags(pwd string) (*xt.Job, *flags) { job := &xt.Job{} flags := &flags{} - flag.BoolVarP(&flags.PrintVer, "version", "v", false, "Print application version and exit") + flag.BoolVarP(&flags.PrintVer, "version", "v", false, "Print application version, and supported extensions.") // These cli options create 1 job. Using job files creates N jobs. - flag.StringVarP(&job.Output, "output", "o", pwd, "Output directory, default is current directory") - flag.UintVarP(&job.MaxDepth, "max-depth", "d", 0, "Maximum folder depth to recursively search for archives.") - flag.UintVarP(&job.MinDepth, "min-depth", "m", 0, "Minimum folder depth to recursively search for archives.") + flag.StringVarP(&job.Output, "output", "o", pwd, "Output directory, default is current directory.") + flag.Uint16VarP(&job.MaxDepth, "max-depth", "d", 0, "Maximum folder depth to recursively search for archives.") + flag.Uint16VarP(&job.MinDepth, "min-depth", "m", 0, "Minimum folder depth to recursively search for archives.") flag.StringSliceVarP(&job.Include, "extension", "e", nil, "Only extract files with these extensions.") flag.StringSliceVarP(&job.Passwords, "password", "P", nil, "Attempt these passwords for rar and 7zip archives.") + flag.BoolVarP(&job.SquashRoot, "squash-root", "S", false, + "If archive contains only 1 folder at in the root, its contents are moved into output folder.") + flag.BoolVarP(&job.DebugLog, "debug", "D", false, "Enable debug output.") flag.StringSliceVarP(&flags.JobFiles, "job-file", "j", nil, "Read additional extraction jobs from these files.") flag.Parse() // Preserve paths? diff --git a/pkg/xt/filemode.go b/pkg/xt/filemode.go index 78ee585..5dcec96 100644 --- a/pkg/xt/filemode.go +++ b/pkg/xt/filemode.go @@ -8,7 +8,7 @@ import ( ) // FileMode is used to unmarshal a unix file mode from the config file. -type FileMode os.FileMode +type FileMode os.FileMode //nolint:recvcheck // it works this way... // UnmarshalText turns a unix file mode, wrapped in quotes or not, into a usable os.FileMode. func (f *FileMode) UnmarshalText(text []byte) error { diff --git a/pkg/xt/job.go b/pkg/xt/job.go index c4abab0..95f69c7 100644 --- a/pkg/xt/job.go +++ b/pkg/xt/job.go @@ -2,21 +2,24 @@ package xt import ( "fmt" + "log" "golift.io/cnfgfile" ) // Job defines the input data for one extraction run. type Job struct { - Paths []string `json:"paths" yaml:"paths" xml:"path" toml:"paths"` - Output string `json:"output" yaml:"output" xml:"output" toml:"output"` - Passwords []string `json:"passwords" yaml:"passwords" xml:"password" toml:"passwords"` - Exclude []string `json:"excludeSuffix" yaml:"excludeSuffix" xml:"exclude_suffix" toml:"exclude_suffix"` - Include []string `json:"includeSuffix" yaml:"includeSuffix" xml:"include_suffix" toml:"include_suffix"` - MaxDepth uint `json:"maxDepth" yaml:"maxDepth" xml:"max_depth" toml:"max_depth"` - MinDepth uint `json:"minDepth" yaml:"minDepth" xml:"min_depth" toml:"min_depth"` - DirMode FileMode `json:"dirMode" yaml:"dirMode" xml:"dir_mode" toml:"dir_mode"` - FileMode FileMode `json:"fileMode" yaml:"fileMode" xml:"file_mode" toml:"file_mode"` + Paths []string `json:"paths" toml:"paths" xml:"path" yaml:"paths"` + Output string `json:"output" toml:"output" xml:"output" yaml:"output"` + Passwords []string `json:"passwords" toml:"passwords" xml:"password" yaml:"passwords"` + Exclude []string `json:"excludeSuffix" toml:"exclude_suffix" xml:"exclude_suffix" yaml:"excludeSuffix"` + Include []string `json:"includeSuffix" toml:"include_suffix" xml:"include_suffix" yaml:"includeSuffix"` + MaxDepth uint16 `json:"maxDepth" toml:"max_depth" xml:"max_depth" yaml:"maxDepth"` + MinDepth uint16 `json:"minDepth" toml:"min_depth" xml:"min_depth" yaml:"minDepth"` + DirMode FileMode `json:"dirMode" toml:"dir_mode" xml:"dir_mode" yaml:"dirMode"` + FileMode FileMode `json:"fileMode" toml:"file_mode" xml:"file_mode" yaml:"fileMode"` + SquashRoot bool `json:"squashRoot" toml:"squash_root" xml:"squash_root" yaml:"squashRoot"` + DebugLog bool `json:"debugLog" toml:"debug_log" xml:"debug_log" yaml:"debugLog"` } // ParseJobs checks for and reads more jobs in from 0 or more job files. @@ -36,6 +39,30 @@ func ParseJobs(jobFiles []string) ([]*Job, error) { return jobs, nil } +func (j *Job) String() string { + j.fixModes() + + sSfx := "" + if len(j.Paths) > 1 { + sSfx = "s" + } + + return fmt.Sprintf("%d path%s, f/d-mode:%s/%s, min/max-depth: %d/%d output: %s", + len(j.Paths), sSfx, j.FileMode, j.DirMode, j.MinDepth, j.MaxDepth, j.Output) +} + +// Debugf prints a log message if debug is enabled. +func (j *Job) Debugf(format string, vars ...any) { + if j.DebugLog { + log.Printf("[DEBUG] "+format, vars...) + } +} + +// Printf wraps log.Printf. +func (j *Job) Printf(format string, vars ...any) { + log.Printf(format, vars...) +} + func (j *Job) fixModes() { const ( defaultFileMode = 0o644 @@ -50,15 +77,3 @@ func (j *Job) fixModes() { j.FileMode = defaultFileMode } } - -func (j *Job) String() string { - j.fixModes() - - sSfx := "" - if len(j.Paths) > 1 { - sSfx = "s" - } - - return fmt.Sprintf("%d path%s, f/d-mode:%s/%s, min/max-depth: %d/%d output: %s", - len(j.Paths), sSfx, j.FileMode, j.DirMode, j.MinDepth, j.MaxDepth, j.Output) -} diff --git a/pkg/xt/xt.go b/pkg/xt/xt.go index 45f1cce..78494e8 100644 --- a/pkg/xt/xt.go +++ b/pkg/xt/xt.go @@ -1,3 +1,4 @@ +// Package xt provides the interface to extract archive files based on job parameters. package xt import ( @@ -9,6 +10,7 @@ import ( "golift.io/xtractr" ) +// Extract runs a job immediately. func Extract(job *Job) { archives := job.getArchives() if len(archives) == 0 { @@ -29,23 +31,31 @@ func Extract(job *Job) { count++ log.Printf("==> Extracting Archive (%d/%d): %s", count, total, fileName) + file := &xtractr.XFile{ + FilePath: fileName, // Path to archive being extracted. + OutputDir: job.Output, // Folder to extract archive into. + FileMode: job.FileMode.Mode(), // Write files with this mode. + DirMode: job.DirMode.Mode(), // Write folders with this mode. + Passwords: job.Passwords, // (RAR/7zip) Archive password(s). + SquashRoot: job.SquashRoot, // Remove single root folder? + } + + file.SetLogger(job) + start := time.Now() - size, files, _, err := xtractr.ExtractFile(&xtractr.XFile{ - FilePath: fileName, // Path to archive being extracted. - OutputDir: job.Output, // Folder to extract archive into. - FileMode: job.FileMode.Mode(), // Write files with this mode. - DirMode: job.DirMode.Mode(), // Write folders with this mode. - Passwords: job.Passwords, // (RAR/7zip) Archive password(s). - }) + size, files, _, err := xtractr.ExtractFile(file) if err != nil { log.Printf("[ERROR] Archive: %s: %v", fileName, err) continue } - elapsed := time.Since(start).Round(time.Millisecond) - log.Printf("==> Extracted Archive %s in %v: bytes: %d, files: %d", fileName, elapsed, size, len(files)) - log.Printf("==> Files:\n - %s", strings.Join(files, "\n - ")) + log.Printf("==> Extracted Archive %s in %v: bytes: %d, files: %d", + fileName, time.Since(start).Round(time.Millisecond), size, len(files)) + + if len(files) > 0 { + log.Printf("==> Files:\n - %s", strings.Join(files, "\n - ")) + } } } }