Compare commits
3 Commits
fix/deps
...
fixBrokenR
| Author | SHA1 | Date | |
|---|---|---|---|
| a51a3b57f6 | |||
| 683a3bbf3d | |||
| ae9e49e1b5 |
@ -281,13 +281,21 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
}
|
||||
|
||||
func getLatestVersionOrCommit(app appPkg.App) (string, error) {
|
||||
versions, err := app.Recipe.Tags()
|
||||
recipeVersions, warnings, err := app.Recipe.GetRecipeVersions()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(versions) > 0 && !internal.Chaos {
|
||||
return versions[len(versions)-1], nil
|
||||
for _, warning := range warnings {
|
||||
log.Warn(warning)
|
||||
}
|
||||
|
||||
if len(recipeVersions) > 0 && !internal.Chaos {
|
||||
latest := recipeVersions[len(recipeVersions)-1]
|
||||
for tag := range latest {
|
||||
log.Debug(i18n.G("selected latest recipe version: %s (from %d available versions)", tag, len(recipeVersions)))
|
||||
return tag, nil
|
||||
}
|
||||
}
|
||||
|
||||
head, err := app.Recipe.Head()
|
||||
|
||||
@ -113,13 +113,6 @@ Use "--status/-S" flag to query all servers for the live deployment status.`),
|
||||
totalAppsCount++
|
||||
|
||||
if status {
|
||||
if err := app.Recipe.EnsureUpToDate(); err != nil {
|
||||
log.Warnf(
|
||||
"Failed to ensure repo is up to date for recipe: %s err: %s",
|
||||
app.Recipe.Name,
|
||||
err,
|
||||
)
|
||||
}
|
||||
status := i18n.G("unknown")
|
||||
version := i18n.G("unknown")
|
||||
chaos := i18n.G("unknown")
|
||||
|
||||
@ -98,10 +98,14 @@ var AppNewCommand = &cobra.Command{
|
||||
var recipeVersions recipePkg.RecipeVersions
|
||||
if recipeVersion == "" {
|
||||
var err error
|
||||
recipeVersions, _, err = recipe.GetRecipeVersions()
|
||||
var warnings []string
|
||||
recipeVersions, warnings, err = recipe.GetRecipeVersions()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, warning := range warnings {
|
||||
log.Warn(warning)
|
||||
}
|
||||
}
|
||||
|
||||
if len(recipeVersions) > 0 {
|
||||
@ -110,6 +114,8 @@ var AppNewCommand = &cobra.Command{
|
||||
recipeVersion = tag
|
||||
}
|
||||
|
||||
log.Debug(i18n.G("selected recipe version: %s (from %d available versions)", recipeVersion, len(recipeVersions)))
|
||||
|
||||
if _, err := recipe.EnsureVersion(recipeVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
103
go.mod
103
go.mod
@ -1,6 +1,8 @@
|
||||
module coopcloud.tech/abra
|
||||
|
||||
go 1.24.2
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca
|
||||
@ -10,18 +12,17 @@ require (
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/charmbracelet/log v0.4.2
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/cli v29.1.5+incompatible
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
github.com/docker/cli v28.4.0+incompatible
|
||||
github.com/docker/docker v28.4.0+incompatible
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/go-git/go-git/v5 v5.16.4
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/leonelquinteros/gotext v1.7.2
|
||||
github.com/moby/moby/client v0.2.1
|
||||
github.com/moby/sys/signal v0.7.1
|
||||
github.com/moby/term v0.5.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/schollz/progressbar/v3 v3.19.0
|
||||
golang.org/x/term v0.39.0
|
||||
github.com/schollz/progressbar/v3 v3.18.0
|
||||
golang.org/x/term v0.35.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gotest.tools/v3 v3.5.2
|
||||
)
|
||||
@ -29,7 +30,7 @@ require (
|
||||
require (
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/BurntSushi/toml v1.6.0 // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
@ -37,21 +38,19 @@ require (
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.4 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.3.2 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.10.2 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.7.0 // indirect
|
||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.2 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
@ -62,20 +61,20 @@ require (
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.7.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.1 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
||||
github.com/klauspost/compress v1.18.3 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
@ -83,14 +82,14 @@ require (
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.2.0 // indirect
|
||||
github.com/moby/moby/api v1.53.0-rc.1 // indirect
|
||||
github.com/moby/go-archive v0.1.0 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/morikuni/aec v1.1.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
@ -101,40 +100,39 @@ require (
|
||||
github.com/pjbgf/sha1cd v0.5.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.5 // indirect
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/skeema/knownhosts v1.3.2 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect
|
||||
go.opentelemetry.io/otel v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.47.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect
|
||||
google.golang.org/grpc v1.78.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/time v0.13.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect
|
||||
google.golang.org/grpc v1.75.1 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
@ -143,7 +141,7 @@ require (
|
||||
github.com/containers/image v3.0.2+incompatible
|
||||
github.com/containers/storage v1.38.2 // indirect
|
||||
github.com/decentral1se/passgen v1.0.1
|
||||
github.com/docker/docker-credential-helpers v0.9.5 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
@ -153,8 +151,9 @@ require (
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
golang.org/x/sys v0.40.0
|
||||
golang.org/x/sys v0.36.0
|
||||
)
|
||||
|
||||
273
go.sum
273
go.sum
@ -49,8 +49,8 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
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/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
@ -85,6 +85,7 @@ github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@ -103,12 +104,14 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
|
||||
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
|
||||
github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
@ -117,6 +120,7 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR
|
||||
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
@ -125,29 +129,26 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
|
||||
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
|
||||
github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI=
|
||||
github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
|
||||
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
|
||||
github.com/charmbracelet/x/ansi v0.11.3 h1:6DcVaqWI82BBVM/atTyq6yBoRLZFBsnoDoX9GCu2YOI=
|
||||
github.com/charmbracelet/x/ansi v0.11.3/go.mod h1:yI7Zslym9tCJcedxz5+WBq+eUGMJT0bM06Fqy1/Y4dI=
|
||||
github.com/charmbracelet/x/ansi v0.11.4 h1:6G65PLu6HjmE858CnTUQY1LXT3ZUWwfvqEROLF8vqHI=
|
||||
github.com/charmbracelet/x/ansi v0.11.4/go.mod h1:/5AZ+UfWExW3int5H5ugnsG/PWjNcSQcwYsHBlPFQN4=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA=
|
||||
github.com/charmbracelet/x/ansi v0.10.2 h1:ith2ArZS0CJG30cIUfID1LXN7ZFXRCww6RUvAPA+Pzw=
|
||||
github.com/charmbracelet/x/ansi v0.10.2/go.mod h1:HbLdJjQH4UH4AqA2HpRWuWNluRE6zxJH/yteYEYCFa8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
|
||||
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
|
||||
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
||||
@ -163,16 +164,11 @@ github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ
|
||||
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/clipperhouse/displaywidth v0.6.2 h1:ZDpTkFfpHOKte4RG5O/BOyf3ysnvFswpyYrV7z2uAKo=
|
||||
github.com/clipperhouse/displaywidth v0.6.2/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
||||
github.com/clipperhouse/displaywidth v0.7.0 h1:QNv1GYsnLX9QBrcWUtMlogpTXuM5FVnBwKWp1O5NwmE=
|
||||
github.com/clipperhouse/displaywidth v0.7.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ=
|
||||
github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
@ -298,12 +294,12 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||
github.com/cyphar/filepath-securejoin v0.5.0 h1:hIAhkRBMQ8nIeuVwcAoymp7MY4oherZdAxD+m0u9zaw=
|
||||
github.com/cyphar/filepath-securejoin v0.5.0/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
||||
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
|
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
||||
@ -313,6 +309,7 @@ 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/decentral1se/passgen v1.0.1 h1:j2AxK/kHKxDHWZZfkJj8Wgae9+O+DYEqR5sjKthIYKA=
|
||||
github.com/decentral1se/passgen v1.0.1/go.mod h1:530V+lNoPhKtkrX2fIVsIfLhkl47CuiOM7HRgi7C+SU=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
@ -321,25 +318,21 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v29.1.3+incompatible h1:+kz9uDWgs+mAaIZojWfFt4d53/jv0ZUOOoSh5ZnH36c=
|
||||
github.com/docker/cli v29.1.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v29.1.4+incompatible h1:AI8fwZhqsAsrqZnVv9h6lbexeW/LzNTasf6A4vcNN8M=
|
||||
github.com/docker/cli v29.1.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v29.1.5+incompatible h1:GckbANUt3j+lsnQ6eCcQd70mNSOismSHWt8vk2AX8ao=
|
||||
github.com/docker/cli v29.1.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY=
|
||||
github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk=
|
||||
github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
||||
github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI=
|
||||
github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
|
||||
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
@ -358,6 +351,7 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
@ -373,6 +367,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
@ -394,12 +389,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM=
|
||||
github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y=
|
||||
github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@ -408,8 +403,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
|
||||
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@ -428,11 +423,10 @@ github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
@ -441,12 +435,14 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
|
||||
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@ -481,6 +477,7 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@ -518,6 +515,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
@ -531,10 +529,10 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 h1:kEISI/Gx67NzH3nJxAmY/dGac80kKZgZt134u7Y/k1s=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4/go.mod h1:6Nz966r3vQYCqIzWsuEl9d7cf7mRhtDmm++sOxlnfxI=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
@ -564,6 +562,9 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf
|
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
|
||||
@ -575,6 +576,7 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
@ -587,10 +589,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
||||
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
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/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
@ -612,9 +612,11 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc=
|
||||
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
|
||||
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@ -637,28 +639,29 @@ github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhg
|
||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
|
||||
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
|
||||
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/moby/api v1.53.0-rc.1 h1:M5SUwRbTrNy+plCTiV6gn4ZiN/Csynk0imIsUmOgHGI=
|
||||
github.com/moby/moby/api v1.53.0-rc.1/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k=
|
||||
github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
@ -685,9 +688,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
|
||||
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
@ -755,6 +757,7 @@ github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqi
|
||||
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
|
||||
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
|
||||
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
@ -769,6 +772,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
@ -789,10 +793,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
||||
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
@ -804,8 +806,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
@ -814,15 +816,13 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
|
||||
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/schollz/progressbar/v3 v3.19.0 h1:Ea18xuIRQXLAUidVDox3AbwfUhD0/1IvohyTutOIFoc=
|
||||
github.com/schollz/progressbar/v3 v3.19.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
|
||||
github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA=
|
||||
github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
|
||||
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
@ -839,24 +839,26 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
|
||||
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
@ -864,6 +866,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
|
||||
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@ -885,6 +888,8 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtse
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
|
||||
github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
|
||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
@ -936,29 +941,29 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ=
|
||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 h1:cEf8jF6WbuGQWUVcqgyWtTR0kOOAWY1DYZ+UhvdmQPw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0/go.mod h1:k1lzV5n5U3HkGvTCJHraTAGJ7MqsgL1wrGwTj1Isfiw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE=
|
||||
go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@ -967,27 +972,26 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
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=
|
||||
@ -998,10 +1002,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@ -1065,10 +1067,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -1164,16 +1164,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -1183,18 +1180,16 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
|
||||
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -1290,15 +1285,12 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:Xa7le7qx2vmqB/SzWUBa7KdMjpdpAHlh5QCSnjessQk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3 h1:X9z6obt+cWRX8XjDVOn+SZWhWe5kZHm46TThU9j+jss=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3/go.mod h1:dd646eSK+Dk9kxVBl1nChEOhJPtMXriCcVb4x3o6J+E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 h1:8XJ4pajGwOlasW+L13MnEGA8W4115jJySQtVfS2/IBU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 h1:i8QOKZfYg6AbGVZzUAY3LrNWCKF8O6zFisU9Wl9RER4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@ -1316,8 +1308,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
||||
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
||||
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
|
||||
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@ -1331,10 +1323,11 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@ -1349,6 +1342,7 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
@ -1368,7 +1362,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
@ -1410,8 +1403,6 @@ k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H
|
||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@ -2,18 +2,13 @@ package context
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"coopcloud.tech/abra/pkg/i18n"
|
||||
"github.com/docker/cli/cli/command"
|
||||
dConfig "github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/context"
|
||||
"github.com/docker/cli/cli/context/docker"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
contextStore "github.com/docker/cli/cli/context/store"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
dopts "github.com/docker/cli/opts"
|
||||
"github.com/moby/moby/client"
|
||||
)
|
||||
|
||||
func NewDefaultDockerContextStore() *command.ContextStoreWithDefault {
|
||||
@ -26,107 +21,13 @@ func NewDefaultDockerContextStore() *command.ContextStoreWithDefault {
|
||||
dockerContextStore := &command.ContextStoreWithDefault{
|
||||
Store: store,
|
||||
Resolver: func() (*command.DefaultContext, error) {
|
||||
return resolveDefaultContext(opts, storeConfig)
|
||||
return command.ResolveDefaultContext(opts, storeConfig)
|
||||
},
|
||||
}
|
||||
|
||||
return dockerContextStore
|
||||
}
|
||||
|
||||
// resolveDefaultContext creates a Metadata for the current CLI invocation parameters
|
||||
func resolveDefaultContext(opts *cliflags.ClientOptions, config store.Config) (*command.DefaultContext, error) {
|
||||
contextTLSData := store.ContextTLSData{
|
||||
Endpoints: make(map[string]store.EndpointTLSData),
|
||||
}
|
||||
|
||||
contextMetadata := store.Metadata{
|
||||
Endpoints: make(map[string]any),
|
||||
Metadata: command.DockerContext{
|
||||
Description: "",
|
||||
},
|
||||
Name: "default",
|
||||
}
|
||||
|
||||
dockerEP, err := resolveDefaultDockerEndpoint(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contextMetadata.Endpoints[docker.DockerEndpoint] = dockerEP.EndpointMeta
|
||||
if dockerEP.TLSData != nil {
|
||||
contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerEP.TLSData.ToStoreTLSData()
|
||||
}
|
||||
|
||||
if err := config.ForeachEndpointType(func(n string, get store.TypeGetter) error {
|
||||
if n == docker.DockerEndpoint { // handled above
|
||||
return nil
|
||||
}
|
||||
ep := get()
|
||||
if i, ok := ep.(command.EndpointDefaultResolver); ok {
|
||||
meta, tls, err := i.ResolveDefault()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if meta == nil {
|
||||
return nil
|
||||
}
|
||||
contextMetadata.Endpoints[n] = meta
|
||||
if tls != nil {
|
||||
contextTLSData.Endpoints[n] = *tls
|
||||
}
|
||||
}
|
||||
// Nothing to be done
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &command.DefaultContext{Meta: contextMetadata, TLS: contextTLSData}, nil
|
||||
}
|
||||
|
||||
// Resolve the Docker endpoint for the default context (based on config, env vars and CLI flags)
|
||||
func resolveDefaultDockerEndpoint(opts *cliflags.ClientOptions) (docker.Endpoint, error) {
|
||||
// defaultToTLS determines whether we should use a TLS host as default
|
||||
// if nothing was configured by the user.
|
||||
defaultToTLS := opts.TLSOptions != nil
|
||||
host, err := getServerHost(opts.Hosts, defaultToTLS)
|
||||
if err != nil {
|
||||
return docker.Endpoint{}, err
|
||||
}
|
||||
|
||||
var (
|
||||
skipTLSVerify bool
|
||||
tlsData *context.TLSData
|
||||
)
|
||||
|
||||
if opts.TLSOptions != nil {
|
||||
skipTLSVerify = opts.TLSOptions.InsecureSkipVerify
|
||||
tlsData, err = context.TLSDataFromFiles(opts.TLSOptions.CAFile, opts.TLSOptions.CertFile, opts.TLSOptions.KeyFile)
|
||||
if err != nil {
|
||||
return docker.Endpoint{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return docker.Endpoint{
|
||||
EndpointMeta: docker.EndpointMeta{
|
||||
Host: host,
|
||||
SkipTLSVerify: skipTLSVerify,
|
||||
},
|
||||
TLSData: tlsData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getServerHost(hosts []string, defaultToTLS bool) (string, error) {
|
||||
switch len(hosts) {
|
||||
case 0:
|
||||
return dopts.ParseHost(defaultToTLS, os.Getenv(client.EnvOverrideHost))
|
||||
case 1:
|
||||
return dopts.ParseHost(defaultToTLS, hosts[0])
|
||||
default:
|
||||
return "", errors.New("specify only one -H")
|
||||
}
|
||||
}
|
||||
|
||||
func GetContextEndpoint(ctx contextStore.Metadata) (string, error) {
|
||||
endpointmeta, ok := ctx.Endpoints["docker"].(context.EndpointMetaBase)
|
||||
if !ok {
|
||||
|
||||
@ -403,15 +403,18 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
|
||||
Branch: plumbing.ReferenceName(ref.Name()),
|
||||
}
|
||||
if err := worktree.Checkout(checkOutOpts); err != nil {
|
||||
log.Debug(i18n.G("failed to check out %s in %s", tag, r.Dir))
|
||||
return err
|
||||
log.Debug(i18n.G("failed to check out %s in %s: %s", tag, r.Dir, err))
|
||||
warnMsg = append(warnMsg, i18n.G("skipping tag %s: checkout failed: %s", tag, err))
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debug(i18n.G("git checkout: %s in %s", ref.Name(), r.Dir))
|
||||
|
||||
config, err := r.GetComposeConfig(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Debug(i18n.G("failed to get compose config for %s: %s", tag, err))
|
||||
warnMsg = append(warnMsg, i18n.G("skipping tag %s: invalid compose config: %s", tag, err))
|
||||
return nil
|
||||
}
|
||||
|
||||
versionMeta := make(map[string]ServiceMeta)
|
||||
@ -419,7 +422,9 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
|
||||
|
||||
img, err := reference.ParseNormalizedNamed(service.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Debug(i18n.G("failed to parse image for %s in %s: %s", service.Name, tag, err))
|
||||
warnMsg = append(warnMsg, i18n.G("skipping tag %s: invalid image reference in service %s: %s", tag, service.Name, err))
|
||||
return nil
|
||||
}
|
||||
|
||||
path := reference.Path(img)
|
||||
@ -445,6 +450,7 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Warn(i18n.G("GetRecipeVersions encountered error for %s: %s (collected %d versions)", r.Name, err, len(versions)))
|
||||
return versions, warnMsg, nil
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"github.com/docker/cli/cli/connhelper/ssh"
|
||||
"github.com/docker/cli/cli/context/docker"
|
||||
dCliContextStore "github.com/docker/cli/cli/context/store"
|
||||
dClient "github.com/moby/moby/client"
|
||||
dClient "github.com/docker/docker/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@ -73,7 +73,7 @@ func getDockerEndpoint(host string) (docker.Endpoint, error) {
|
||||
if err != nil {
|
||||
return docker.Endpoint{}, err
|
||||
}
|
||||
if _, err := dClient.New(opts...); err != nil {
|
||||
if _, err := dClient.NewClientWithOpts(opts...); err != nil {
|
||||
return docker.Endpoint{}, err
|
||||
}
|
||||
return ep, nil
|
||||
|
||||
@ -19,7 +19,7 @@ import (
|
||||
"coopcloud.tech/abra/pkg/ui"
|
||||
"coopcloud.tech/abra/pkg/upstream/convert"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/command/stack/formatter"
|
||||
composetypes "github.com/docker/cli/cli/compose/types"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
||||
@ -67,16 +67,6 @@ teardown(){
|
||||
assert_output --partial "$TEST_SERVER"
|
||||
assert_output --partial "$TEST_APP_DOMAIN"
|
||||
assert_output --partial "deployed"
|
||||
assert_output --partial "latest"
|
||||
|
||||
_remove_tags
|
||||
|
||||
run $ABRA app ls --status
|
||||
assert_success
|
||||
assert_output --partial "$TEST_SERVER"
|
||||
assert_output --partial "$TEST_APP_DOMAIN"
|
||||
assert_output --partial "deployed"
|
||||
assert_output --partial "latest"
|
||||
}
|
||||
|
||||
@test "filter by server" {
|
||||
|
||||
@ -17,12 +17,7 @@ _remove_tags(){
|
||||
|
||||
run bash -c 'git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag -l | wc -l'
|
||||
assert_success
|
||||
# If this was done without the --regexp I get this error:
|
||||
# -- output differs --
|
||||
# expected : 0
|
||||
# actual : 0
|
||||
# --
|
||||
assert_output --regexp '[[:space:]]0'
|
||||
assert_output '0'
|
||||
}
|
||||
|
||||
_reset_tags() {
|
||||
|
||||
2
vendor/github.com/BurntSushi/toml/README.md
generated
vendored
2
vendor/github.com/BurntSushi/toml/README.md
generated
vendored
@ -1,7 +1,7 @@
|
||||
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
|
||||
reflection interface similar to Go's standard library `json` and `xml` packages.
|
||||
|
||||
Compatible with TOML version [v1.1.0](https://toml.io/en/v1.1.0).
|
||||
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
|
||||
|
||||
Documentation: https://pkg.go.dev/github.com/BurntSushi/toml
|
||||
|
||||
|
||||
9
vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
9
vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
@ -206,13 +206,6 @@ func markDecodedRecursive(md *MetaData, tmap map[string]any) {
|
||||
markDecodedRecursive(md, tmap)
|
||||
md.context = md.context[0 : len(md.context)-1]
|
||||
}
|
||||
if tarr, ok := tmap[key].([]map[string]any); ok {
|
||||
for _, elm := range tarr {
|
||||
md.context = append(md.context, key)
|
||||
markDecodedRecursive(md, elm)
|
||||
md.context = md.context[0 : len(md.context)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -430,7 +423,7 @@ func (md *MetaData) unifyString(data any, rv reflect.Value) error {
|
||||
if i, ok := data.(int64); ok {
|
||||
rv.SetString(strconv.FormatInt(i, 10))
|
||||
} else if f, ok := data.(float64); ok {
|
||||
rv.SetString(strconv.FormatFloat(f, 'g', -1, 64))
|
||||
rv.SetString(strconv.FormatFloat(f, 'f', -1, 64))
|
||||
} else {
|
||||
return md.badtype("string", data)
|
||||
}
|
||||
|
||||
79
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
79
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
@ -228,9 +228,9 @@ func (enc *Encoder) eElement(rv reflect.Value) {
|
||||
}
|
||||
switch v.Location() {
|
||||
default:
|
||||
enc.write(v.Format(format))
|
||||
enc.wf(v.Format(format))
|
||||
case internal.LocalDatetime, internal.LocalDate, internal.LocalTime:
|
||||
enc.write(v.In(time.UTC).Format(format))
|
||||
enc.wf(v.In(time.UTC).Format(format))
|
||||
}
|
||||
return
|
||||
case Marshaler:
|
||||
@ -279,40 +279,40 @@ func (enc *Encoder) eElement(rv reflect.Value) {
|
||||
case reflect.String:
|
||||
enc.writeQuoted(rv.String())
|
||||
case reflect.Bool:
|
||||
enc.write(strconv.FormatBool(rv.Bool()))
|
||||
enc.wf(strconv.FormatBool(rv.Bool()))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
enc.write(strconv.FormatInt(rv.Int(), 10))
|
||||
enc.wf(strconv.FormatInt(rv.Int(), 10))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
enc.write(strconv.FormatUint(rv.Uint(), 10))
|
||||
enc.wf(strconv.FormatUint(rv.Uint(), 10))
|
||||
case reflect.Float32:
|
||||
f := rv.Float()
|
||||
if math.IsNaN(f) {
|
||||
if math.Signbit(f) {
|
||||
enc.write("-")
|
||||
enc.wf("-")
|
||||
}
|
||||
enc.write("nan")
|
||||
enc.wf("nan")
|
||||
} else if math.IsInf(f, 0) {
|
||||
if math.Signbit(f) {
|
||||
enc.write("-")
|
||||
enc.wf("-")
|
||||
}
|
||||
enc.write("inf")
|
||||
enc.wf("inf")
|
||||
} else {
|
||||
enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 32)))
|
||||
enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 32)))
|
||||
}
|
||||
case reflect.Float64:
|
||||
f := rv.Float()
|
||||
if math.IsNaN(f) {
|
||||
if math.Signbit(f) {
|
||||
enc.write("-")
|
||||
enc.wf("-")
|
||||
}
|
||||
enc.write("nan")
|
||||
enc.wf("nan")
|
||||
} else if math.IsInf(f, 0) {
|
||||
if math.Signbit(f) {
|
||||
enc.write("-")
|
||||
enc.wf("-")
|
||||
}
|
||||
enc.write("inf")
|
||||
enc.wf("inf")
|
||||
} else {
|
||||
enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 64)))
|
||||
enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 64)))
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
enc.eArrayOrSliceElement(rv)
|
||||
@ -330,32 +330,27 @@ func (enc *Encoder) eElement(rv reflect.Value) {
|
||||
// By the TOML spec, all floats must have a decimal with at least one number on
|
||||
// either side.
|
||||
func floatAddDecimal(fstr string) string {
|
||||
for _, c := range fstr {
|
||||
if c == 'e' { // Exponent syntax
|
||||
return fstr
|
||||
}
|
||||
if c == '.' {
|
||||
return fstr
|
||||
}
|
||||
if !strings.Contains(fstr, ".") {
|
||||
return fstr + ".0"
|
||||
}
|
||||
return fstr + ".0"
|
||||
return fstr
|
||||
}
|
||||
|
||||
func (enc *Encoder) writeQuoted(s string) {
|
||||
enc.write(`"` + dblQuotedReplacer.Replace(s) + `"`)
|
||||
enc.wf("\"%s\"", dblQuotedReplacer.Replace(s))
|
||||
}
|
||||
|
||||
func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
|
||||
length := rv.Len()
|
||||
enc.write("[")
|
||||
enc.wf("[")
|
||||
for i := 0; i < length; i++ {
|
||||
elem := eindirect(rv.Index(i))
|
||||
enc.eElement(elem)
|
||||
if i != length-1 {
|
||||
enc.write(", ")
|
||||
enc.wf(", ")
|
||||
}
|
||||
}
|
||||
enc.write("]")
|
||||
enc.wf("]")
|
||||
}
|
||||
|
||||
func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
||||
@ -368,7 +363,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
||||
continue
|
||||
}
|
||||
enc.newline()
|
||||
enc.writef("%s[[%s]]", enc.indentStr(key), key)
|
||||
enc.wf("%s[[%s]]", enc.indentStr(key), key)
|
||||
enc.newline()
|
||||
enc.eMapOrStruct(key, trv, false)
|
||||
}
|
||||
@ -381,7 +376,7 @@ func (enc *Encoder) eTable(key Key, rv reflect.Value) {
|
||||
enc.newline()
|
||||
}
|
||||
if len(key) > 0 {
|
||||
enc.writef("%s[%s]", enc.indentStr(key), key)
|
||||
enc.wf("%s[%s]", enc.indentStr(key), key)
|
||||
enc.newline()
|
||||
}
|
||||
enc.eMapOrStruct(key, rv, false)
|
||||
@ -427,7 +422,7 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
|
||||
if inline {
|
||||
enc.writeKeyValue(Key{mapKey.String()}, val, true)
|
||||
if trailC || i != len(mapKeys)-1 {
|
||||
enc.write(", ")
|
||||
enc.wf(", ")
|
||||
}
|
||||
} else {
|
||||
enc.encode(key.add(mapKey.String()), val)
|
||||
@ -436,12 +431,12 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
|
||||
}
|
||||
|
||||
if inline {
|
||||
enc.write("{")
|
||||
enc.wf("{")
|
||||
}
|
||||
writeMapKeys(mapKeysDirect, len(mapKeysSub) > 0)
|
||||
writeMapKeys(mapKeysSub, false)
|
||||
if inline {
|
||||
enc.write("}")
|
||||
enc.wf("}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -539,7 +534,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
|
||||
if inline {
|
||||
enc.writeKeyValue(Key{keyName}, fieldVal, true)
|
||||
if fieldIndex[0] != totalFields-1 {
|
||||
enc.write(", ")
|
||||
enc.wf(", ")
|
||||
}
|
||||
} else {
|
||||
enc.encode(key.add(keyName), fieldVal)
|
||||
@ -548,14 +543,14 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
|
||||
}
|
||||
|
||||
if inline {
|
||||
enc.write("{")
|
||||
enc.wf("{")
|
||||
}
|
||||
|
||||
l := len(fieldsDirect) + len(fieldsSub)
|
||||
writeFields(fieldsDirect, l)
|
||||
writeFields(fieldsSub, l)
|
||||
if inline {
|
||||
enc.write("}")
|
||||
enc.wf("}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -705,7 +700,7 @@ func isEmpty(rv reflect.Value) bool {
|
||||
|
||||
func (enc *Encoder) newline() {
|
||||
if enc.hasWritten {
|
||||
enc.write("\n")
|
||||
enc.wf("\n")
|
||||
}
|
||||
}
|
||||
|
||||
@ -727,22 +722,14 @@ func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
|
||||
enc.eElement(val)
|
||||
return
|
||||
}
|
||||
enc.writef("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
|
||||
enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
|
||||
enc.eElement(val)
|
||||
if !inline {
|
||||
enc.newline()
|
||||
}
|
||||
}
|
||||
|
||||
func (enc *Encoder) write(s string) {
|
||||
_, err := enc.w.WriteString(s)
|
||||
if err != nil {
|
||||
encPanic(err)
|
||||
}
|
||||
enc.hasWritten = true
|
||||
}
|
||||
|
||||
func (enc *Encoder) writef(format string, v ...any) {
|
||||
func (enc *Encoder) wf(format string, v ...any) {
|
||||
_, err := fmt.Fprintf(enc.w, format, v...)
|
||||
if err != nil {
|
||||
encPanic(err)
|
||||
|
||||
122
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
122
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
@ -13,6 +13,7 @@ type itemType int
|
||||
|
||||
const (
|
||||
itemError itemType = iota
|
||||
itemNIL // used in the parser to indicate no type
|
||||
itemEOF
|
||||
itemText
|
||||
itemString
|
||||
@ -46,13 +47,14 @@ func (p Position) String() string {
|
||||
}
|
||||
|
||||
type lexer struct {
|
||||
input string
|
||||
start int
|
||||
pos int
|
||||
line int
|
||||
state stateFn
|
||||
items chan item
|
||||
esc bool
|
||||
input string
|
||||
start int
|
||||
pos int
|
||||
line int
|
||||
state stateFn
|
||||
items chan item
|
||||
tomlNext bool
|
||||
esc bool
|
||||
|
||||
// Allow for backing up up to 4 runes. This is necessary because TOML
|
||||
// contains 3-rune tokens (""" and ''').
|
||||
@ -88,13 +90,14 @@ func (lx *lexer) nextItem() item {
|
||||
}
|
||||
}
|
||||
|
||||
func lex(input string) *lexer {
|
||||
func lex(input string, tomlNext bool) *lexer {
|
||||
lx := &lexer{
|
||||
input: input,
|
||||
state: lexTop,
|
||||
items: make(chan item, 10),
|
||||
stack: make([]stateFn, 0, 10),
|
||||
line: 1,
|
||||
input: input,
|
||||
state: lexTop,
|
||||
items: make(chan item, 10),
|
||||
stack: make([]stateFn, 0, 10),
|
||||
line: 1,
|
||||
tomlNext: tomlNext,
|
||||
}
|
||||
return lx
|
||||
}
|
||||
@ -105,7 +108,7 @@ func (lx *lexer) push(state stateFn) {
|
||||
|
||||
func (lx *lexer) pop() stateFn {
|
||||
if len(lx.stack) == 0 {
|
||||
panic("BUG in lexer: no states to pop")
|
||||
return lx.errorf("BUG in lexer: no states to pop")
|
||||
}
|
||||
last := lx.stack[len(lx.stack)-1]
|
||||
lx.stack = lx.stack[0 : len(lx.stack)-1]
|
||||
@ -302,8 +305,6 @@ func lexTop(lx *lexer) stateFn {
|
||||
return lexTableStart
|
||||
case eof:
|
||||
if lx.pos > lx.start {
|
||||
// TODO: never reached? I think this can only occur on a bug in the
|
||||
// lexer(?)
|
||||
return lx.errorf("unexpected EOF")
|
||||
}
|
||||
lx.emit(itemEOF)
|
||||
@ -391,6 +392,8 @@ func lexTableNameStart(lx *lexer) stateFn {
|
||||
func lexTableNameEnd(lx *lexer) stateFn {
|
||||
lx.skip(isWhitespace)
|
||||
switch r := lx.next(); {
|
||||
case isWhitespace(r):
|
||||
return lexTableNameEnd
|
||||
case r == '.':
|
||||
lx.ignore()
|
||||
return lexTableNameStart
|
||||
@ -409,7 +412,7 @@ func lexTableNameEnd(lx *lexer) stateFn {
|
||||
// Lexes only one part, e.g. only 'a' inside 'a.b'.
|
||||
func lexBareName(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
if isBareKeyChar(r) {
|
||||
if isBareKeyChar(r, lx.tomlNext) {
|
||||
return lexBareName
|
||||
}
|
||||
lx.backup()
|
||||
@ -417,23 +420,23 @@ func lexBareName(lx *lexer) stateFn {
|
||||
return lx.pop()
|
||||
}
|
||||
|
||||
// lexQuotedName lexes one part of a quoted key or table name. It assumes that
|
||||
// it starts lexing at the quote itself (" or ').
|
||||
// lexBareName lexes one part of a key or table.
|
||||
//
|
||||
// It assumes that at least one valid character for the table has already been
|
||||
// read.
|
||||
//
|
||||
// Lexes only one part, e.g. only '"a"' inside '"a".b'.
|
||||
func lexQuotedName(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexValue)
|
||||
case r == '"':
|
||||
lx.ignore() // ignore the '"'
|
||||
return lexString
|
||||
case r == '\'':
|
||||
lx.ignore() // ignore the "'"
|
||||
return lexRawString
|
||||
|
||||
// TODO: I don't think any of the below conditions can ever be reached?
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexValue)
|
||||
case r == eof:
|
||||
return lx.errorf("unexpected EOF; expected value")
|
||||
default:
|
||||
@ -461,19 +464,17 @@ func lexKeyStart(lx *lexer) stateFn {
|
||||
func lexKeyNameStart(lx *lexer) stateFn {
|
||||
lx.skip(isWhitespace)
|
||||
switch r := lx.peek(); {
|
||||
default:
|
||||
lx.push(lexKeyEnd)
|
||||
return lexBareName
|
||||
case r == '"' || r == '\'':
|
||||
lx.ignore()
|
||||
lx.push(lexKeyEnd)
|
||||
return lexQuotedName
|
||||
|
||||
// TODO: I think these can never be reached?
|
||||
case r == '=' || r == eof:
|
||||
return lx.errorf("unexpected '='")
|
||||
case r == '.':
|
||||
return lx.errorf("unexpected '.'")
|
||||
case r == '"' || r == '\'':
|
||||
lx.ignore()
|
||||
lx.push(lexKeyEnd)
|
||||
return lexQuotedName
|
||||
default:
|
||||
lx.push(lexKeyEnd)
|
||||
return lexBareName
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,7 +485,7 @@ func lexKeyEnd(lx *lexer) stateFn {
|
||||
switch r := lx.next(); {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexKeyEnd)
|
||||
case r == eof: // TODO: never reached
|
||||
case r == eof:
|
||||
return lx.errorf("unexpected EOF; expected key separator '='")
|
||||
case r == '.':
|
||||
lx.ignore()
|
||||
@ -627,7 +628,10 @@ func lexInlineTableValue(lx *lexer) stateFn {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexInlineTableValue)
|
||||
case isNL(r):
|
||||
return lexSkip(lx, lexInlineTableValue)
|
||||
if lx.tomlNext {
|
||||
return lexSkip(lx, lexInlineTableValue)
|
||||
}
|
||||
return lx.errorPrevLine(errLexInlineTableNL{})
|
||||
case r == '#':
|
||||
lx.push(lexInlineTableValue)
|
||||
return lexCommentStart
|
||||
@ -649,7 +653,10 @@ func lexInlineTableValueEnd(lx *lexer) stateFn {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexInlineTableValueEnd)
|
||||
case isNL(r):
|
||||
return lexSkip(lx, lexInlineTableValueEnd)
|
||||
if lx.tomlNext {
|
||||
return lexSkip(lx, lexInlineTableValueEnd)
|
||||
}
|
||||
return lx.errorPrevLine(errLexInlineTableNL{})
|
||||
case r == '#':
|
||||
lx.push(lexInlineTableValueEnd)
|
||||
return lexCommentStart
|
||||
@ -657,7 +664,10 @@ func lexInlineTableValueEnd(lx *lexer) stateFn {
|
||||
lx.ignore()
|
||||
lx.skip(isWhitespace)
|
||||
if lx.peek() == '}' {
|
||||
return lexInlineTableValueEnd
|
||||
if lx.tomlNext {
|
||||
return lexInlineTableValueEnd
|
||||
}
|
||||
return lx.errorf("trailing comma not allowed in inline tables")
|
||||
}
|
||||
return lexInlineTableValue
|
||||
case r == '}':
|
||||
@ -845,6 +855,9 @@ func lexStringEscape(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch r {
|
||||
case 'e':
|
||||
if !lx.tomlNext {
|
||||
return lx.error(errLexEscape{r})
|
||||
}
|
||||
fallthrough
|
||||
case 'b':
|
||||
fallthrough
|
||||
@ -865,6 +878,9 @@ func lexStringEscape(lx *lexer) stateFn {
|
||||
case '\\':
|
||||
return lx.pop()
|
||||
case 'x':
|
||||
if !lx.tomlNext {
|
||||
return lx.error(errLexEscape{r})
|
||||
}
|
||||
return lexHexEscape
|
||||
case 'u':
|
||||
return lexShortUnicodeEscape
|
||||
@ -912,9 +928,19 @@ func lexLongUnicodeEscape(lx *lexer) stateFn {
|
||||
// lexBaseNumberOrDate can differentiate base prefixed integers from other
|
||||
// types.
|
||||
func lexNumberOrDateStart(lx *lexer) stateFn {
|
||||
if lx.next() == '0' {
|
||||
r := lx.next()
|
||||
switch r {
|
||||
case '0':
|
||||
return lexBaseNumberOrDate
|
||||
}
|
||||
|
||||
if !isDigit(r) {
|
||||
// The only way to reach this state is if the value starts
|
||||
// with a digit, so specifically treat anything else as an
|
||||
// error.
|
||||
return lx.errorf("expected a digit but got %q", r)
|
||||
}
|
||||
|
||||
return lexNumberOrDate
|
||||
}
|
||||
|
||||
@ -1170,13 +1196,13 @@ func lexSkip(lx *lexer, nextState stateFn) stateFn {
|
||||
}
|
||||
|
||||
func (s stateFn) String() string {
|
||||
if s == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
name := runtime.FuncForPC(reflect.ValueOf(s).Pointer()).Name()
|
||||
if i := strings.LastIndexByte(name, '.'); i > -1 {
|
||||
name = name[i+1:]
|
||||
}
|
||||
if s == nil {
|
||||
name = "<nil>"
|
||||
}
|
||||
return name + "()"
|
||||
}
|
||||
|
||||
@ -1184,6 +1210,8 @@ func (itype itemType) String() string {
|
||||
switch itype {
|
||||
case itemError:
|
||||
return "Error"
|
||||
case itemNIL:
|
||||
return "NIL"
|
||||
case itemEOF:
|
||||
return "EOF"
|
||||
case itemText:
|
||||
@ -1198,22 +1226,18 @@ func (itype itemType) String() string {
|
||||
return "Float"
|
||||
case itemDatetime:
|
||||
return "DateTime"
|
||||
case itemArray:
|
||||
return "Array"
|
||||
case itemArrayEnd:
|
||||
return "ArrayEnd"
|
||||
case itemTableStart:
|
||||
return "TableStart"
|
||||
case itemTableEnd:
|
||||
return "TableEnd"
|
||||
case itemArrayTableStart:
|
||||
return "ArrayTableStart"
|
||||
case itemArrayTableEnd:
|
||||
return "ArrayTableEnd"
|
||||
case itemKeyStart:
|
||||
return "KeyStart"
|
||||
case itemKeyEnd:
|
||||
return "KeyEnd"
|
||||
case itemArray:
|
||||
return "Array"
|
||||
case itemArrayEnd:
|
||||
return "ArrayEnd"
|
||||
case itemCommentStart:
|
||||
return "CommentStart"
|
||||
case itemInlineTableStart:
|
||||
@ -1242,7 +1266,7 @@ func isDigit(r rune) bool { return r >= '0' && r <= '9' }
|
||||
func isBinary(r rune) bool { return r == '0' || r == '1' }
|
||||
func isOctal(r rune) bool { return r >= '0' && r <= '7' }
|
||||
func isHex(r rune) bool { return (r >= '0' && r <= '9') || (r|0x20 >= 'a' && r|0x20 <= 'f') }
|
||||
func isBareKeyChar(r rune) bool {
|
||||
func isBareKeyChar(r rune, tomlNext bool) bool {
|
||||
return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') ||
|
||||
(r >= '0' && r <= '9') || r == '_' || r == '-'
|
||||
}
|
||||
|
||||
46
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
46
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
@ -3,6 +3,7 @@ package toml
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -16,6 +17,7 @@ type parser struct {
|
||||
context Key // Full key for the current hash in scope.
|
||||
currentKey string // Base key name for everything except hashes.
|
||||
pos Position // Current position in the TOML file.
|
||||
tomlNext bool
|
||||
|
||||
ordered []Key // List of keys in the order that they appear in the TOML data.
|
||||
|
||||
@ -30,6 +32,8 @@ type keyInfo struct {
|
||||
}
|
||||
|
||||
func parse(data string) (p *parser, err error) {
|
||||
_, tomlNext := os.LookupEnv("BURNTSUSHI_TOML_110")
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if pErr, ok := r.(ParseError); ok {
|
||||
@ -69,9 +73,10 @@ func parse(data string) (p *parser, err error) {
|
||||
p = &parser{
|
||||
keyInfo: make(map[string]keyInfo),
|
||||
mapping: make(map[string]any),
|
||||
lx: lex(data),
|
||||
lx: lex(data, tomlNext),
|
||||
ordered: make([]Key, 0),
|
||||
implicits: make(map[string]struct{}),
|
||||
tomlNext: tomlNext,
|
||||
}
|
||||
for {
|
||||
item := p.next()
|
||||
@ -345,14 +350,17 @@ func (p *parser) valueFloat(it item) (any, tomlType) {
|
||||
var dtTypes = []struct {
|
||||
fmt string
|
||||
zone *time.Location
|
||||
next bool
|
||||
}{
|
||||
{time.RFC3339Nano, time.Local},
|
||||
{"2006-01-02T15:04:05.999999999", internal.LocalDatetime},
|
||||
{"2006-01-02", internal.LocalDate},
|
||||
{"15:04:05.999999999", internal.LocalTime},
|
||||
{"2006-01-02T15:04Z07:00", time.Local},
|
||||
{"2006-01-02T15:04", internal.LocalDatetime},
|
||||
{"15:04", internal.LocalTime},
|
||||
{time.RFC3339Nano, time.Local, false},
|
||||
{"2006-01-02T15:04:05.999999999", internal.LocalDatetime, false},
|
||||
{"2006-01-02", internal.LocalDate, false},
|
||||
{"15:04:05.999999999", internal.LocalTime, false},
|
||||
|
||||
// tomlNext
|
||||
{"2006-01-02T15:04Z07:00", time.Local, true},
|
||||
{"2006-01-02T15:04", internal.LocalDatetime, true},
|
||||
{"15:04", internal.LocalTime, true},
|
||||
}
|
||||
|
||||
func (p *parser) valueDatetime(it item) (any, tomlType) {
|
||||
@ -363,6 +371,9 @@ func (p *parser) valueDatetime(it item) (any, tomlType) {
|
||||
err error
|
||||
)
|
||||
for _, dt := range dtTypes {
|
||||
if dt.next && !p.tomlNext {
|
||||
continue
|
||||
}
|
||||
t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone)
|
||||
if err == nil {
|
||||
if missingLeadingZero(it.val, dt.fmt) {
|
||||
@ -633,11 +644,6 @@ func (p *parser) setValue(key string, value any) {
|
||||
// Note that since it has already been defined (as a hash), we don't
|
||||
// want to overwrite it. So our business is done.
|
||||
if p.isArray(keyContext) {
|
||||
if !p.isImplicit(keyContext) {
|
||||
if _, ok := hash[key]; ok {
|
||||
p.panicf("Key '%s' has already been defined.", keyContext)
|
||||
}
|
||||
}
|
||||
p.removeImplicit(keyContext)
|
||||
hash[key] = value
|
||||
return
|
||||
@ -796,8 +802,10 @@ func (p *parser) replaceEscapes(it item, str string) string {
|
||||
b.WriteByte(0x0d)
|
||||
skip = 1
|
||||
case 'e':
|
||||
b.WriteByte(0x1b)
|
||||
skip = 1
|
||||
if p.tomlNext {
|
||||
b.WriteByte(0x1b)
|
||||
skip = 1
|
||||
}
|
||||
case '"':
|
||||
b.WriteByte(0x22)
|
||||
skip = 1
|
||||
@ -807,9 +815,11 @@ func (p *parser) replaceEscapes(it item, str string) string {
|
||||
// The lexer guarantees the correct number of characters are present;
|
||||
// don't need to check here.
|
||||
case 'x':
|
||||
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+4])
|
||||
b.WriteRune(escaped)
|
||||
skip = 3
|
||||
if p.tomlNext {
|
||||
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+4])
|
||||
b.WriteRune(escaped)
|
||||
skip = 3
|
||||
}
|
||||
case 'u':
|
||||
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+6])
|
||||
b.WriteRune(escaped)
|
||||
|
||||
3
vendor/github.com/charmbracelet/colorprofile/.golangci.yml
generated
vendored
3
vendor/github.com/charmbracelet/colorprofile/.golangci.yml
generated
vendored
@ -33,9 +33,6 @@ linters:
|
||||
generated: lax
|
||||
presets:
|
||||
- common-false-positives
|
||||
settings:
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
37
vendor/github.com/charmbracelet/colorprofile/env.go
generated
vendored
37
vendor/github.com/charmbracelet/colorprofile/env.go
generated
vendored
@ -83,8 +83,8 @@ func colorProfile(isatty bool, env environ) (p Profile) {
|
||||
}
|
||||
|
||||
if envNoColor(env) && isatty {
|
||||
if p > ASCII {
|
||||
p = ASCII
|
||||
if p > Ascii {
|
||||
p = Ascii
|
||||
}
|
||||
return //nolint:nakedret
|
||||
}
|
||||
@ -153,24 +153,29 @@ func envColorProfile(env environ) (p Profile) {
|
||||
p = ANSI
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(term, "alacritty"),
|
||||
strings.Contains(term, "contour"),
|
||||
strings.Contains(term, "foot"),
|
||||
strings.Contains(term, "ghostty"),
|
||||
strings.Contains(term, "kitty"),
|
||||
strings.Contains(term, "rio"),
|
||||
strings.Contains(term, "st"),
|
||||
strings.Contains(term, "wezterm"):
|
||||
parts := strings.Split(term, "-")
|
||||
switch parts[0] {
|
||||
case "alacritty",
|
||||
"contour",
|
||||
"foot",
|
||||
"ghostty",
|
||||
"kitty",
|
||||
"rio",
|
||||
"st",
|
||||
"wezterm":
|
||||
return TrueColor
|
||||
case strings.HasPrefix(term, "tmux"), strings.HasPrefix(term, "screen"):
|
||||
case "xterm":
|
||||
if len(parts) > 1 {
|
||||
switch parts[1] {
|
||||
case "ghostty", "kitty":
|
||||
// These terminals can be defined as xterm-TERMNAME
|
||||
return TrueColor
|
||||
}
|
||||
}
|
||||
case "tmux", "screen":
|
||||
if p < ANSI256 {
|
||||
p = ANSI256
|
||||
}
|
||||
case strings.HasPrefix(term, "xterm"):
|
||||
if p < ANSI {
|
||||
p = ANSI
|
||||
}
|
||||
}
|
||||
|
||||
if isCloudShell, _ := strconv.ParseBool(env.get("GOOGLE_CLOUD_SHELL")); isCloudShell {
|
||||
|
||||
26
vendor/github.com/charmbracelet/colorprofile/profile.go
generated
vendored
26
vendor/github.com/charmbracelet/colorprofile/profile.go
generated
vendored
@ -11,12 +11,10 @@ import (
|
||||
type Profile byte
|
||||
|
||||
const (
|
||||
// Unknown is a profile that represents the absence of a profile.
|
||||
Unknown Profile = iota
|
||||
// NoTTY is a profile with no terminal support.
|
||||
NoTTY
|
||||
// ASCII is a profile with no color support.
|
||||
ASCII
|
||||
NoTTY Profile = iota
|
||||
// Ascii is a profile with no color support.
|
||||
Ascii //nolint:revive
|
||||
// ANSI is a profile with 16 colors (4-bit).
|
||||
ANSI
|
||||
// ANSI256 is a profile with 256 colors (8-bit).
|
||||
@ -25,9 +23,6 @@ const (
|
||||
TrueColor
|
||||
)
|
||||
|
||||
// Ascii is an alias for the [ASCII] profile for backwards compatibility.
|
||||
const Ascii = ASCII //nolint:revive
|
||||
|
||||
// String returns the string representation of a Profile.
|
||||
func (p Profile) String() string {
|
||||
switch p {
|
||||
@ -37,13 +32,12 @@ func (p Profile) String() string {
|
||||
return "ANSI256"
|
||||
case ANSI:
|
||||
return "ANSI"
|
||||
case ASCII:
|
||||
case Ascii:
|
||||
return "Ascii"
|
||||
case NoTTY:
|
||||
return "NoTTY"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
var (
|
||||
@ -56,7 +50,7 @@ var (
|
||||
|
||||
// Convert transforms a given Color to a Color supported within the Profile.
|
||||
func (p Profile) Convert(c color.Color) (cc color.Color) {
|
||||
if p <= ASCII {
|
||||
if p <= Ascii {
|
||||
return nil
|
||||
}
|
||||
if p == TrueColor {
|
||||
@ -96,13 +90,11 @@ func (p Profile) Convert(c color.Color) (cc color.Color) {
|
||||
return c
|
||||
|
||||
default:
|
||||
switch p {
|
||||
case ANSI256:
|
||||
if p == ANSI256 {
|
||||
return ansi.Convert256(c)
|
||||
case ANSI:
|
||||
} else if p == ANSI {
|
||||
return ansi.Convert16(c)
|
||||
default:
|
||||
return c
|
||||
}
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
14
vendor/github.com/charmbracelet/colorprofile/writer.go
generated
vendored
14
vendor/github.com/charmbracelet/colorprofile/writer.go
generated
vendored
@ -36,12 +36,12 @@ type Writer struct {
|
||||
|
||||
// Write writes the given text to the underlying writer.
|
||||
func (w *Writer) Write(p []byte) (int, error) {
|
||||
switch {
|
||||
case w.Profile == TrueColor:
|
||||
switch w.Profile {
|
||||
case TrueColor:
|
||||
return w.Forward.Write(p) //nolint:wrapcheck
|
||||
case w.Profile <= NoTTY:
|
||||
case NoTTY:
|
||||
return io.WriteString(w.Forward, ansi.Strip(string(p))) //nolint:wrapcheck
|
||||
case w.Profile == ASCII, w.Profile == ANSI, w.Profile == ANSI256:
|
||||
case Ascii, ANSI, ANSI256:
|
||||
return w.downsample(p)
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid profile: %v", w.Profile)
|
||||
@ -112,7 +112,7 @@ func handleSgr(w *Writer, p *ansi.Parser, buf *bytes.Buffer) {
|
||||
if w.Profile < ANSI {
|
||||
continue
|
||||
}
|
||||
style = style.ForegroundColor(nil)
|
||||
style = style.DefaultForegroundColor()
|
||||
case 40, 41, 42, 43, 44, 45, 46, 47: // 8-bit background color
|
||||
if w.Profile < ANSI {
|
||||
continue
|
||||
@ -132,7 +132,7 @@ func handleSgr(w *Writer, p *ansi.Parser, buf *bytes.Buffer) {
|
||||
if w.Profile < ANSI {
|
||||
continue
|
||||
}
|
||||
style = style.BackgroundColor(nil)
|
||||
style = style.DefaultBackgroundColor()
|
||||
case 58: // 16 or 24-bit underline color
|
||||
var c color.Color
|
||||
if n := ansi.ReadStyleColor(params[i:], &c); n > 0 {
|
||||
@ -146,7 +146,7 @@ func handleSgr(w *Writer, p *ansi.Parser, buf *bytes.Buffer) {
|
||||
if w.Profile < ANSI {
|
||||
continue
|
||||
}
|
||||
style = style.UnderlineColor(nil)
|
||||
style = style.DefaultUnderlineColor()
|
||||
case 90, 91, 92, 93, 94, 95, 96, 97: // 8-bit bright foreground color
|
||||
if w.Profile < ANSI {
|
||||
continue
|
||||
|
||||
6
vendor/github.com/charmbracelet/x/ansi/cursor.go
generated
vendored
6
vendor/github.com/charmbracelet/x/ansi/cursor.go
generated
vendored
@ -261,7 +261,7 @@ func CHA(col int) string {
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CUP.html
|
||||
func CursorPosition(col, row int) string {
|
||||
if row <= 1 && col <= 1 {
|
||||
if row <= 0 && col <= 0 {
|
||||
return CursorHomePosition
|
||||
}
|
||||
|
||||
@ -281,9 +281,7 @@ func CUP(col, row int) string {
|
||||
}
|
||||
|
||||
// CursorHomePosition is a sequence for moving the cursor to the upper left
|
||||
// corner of the scrolling region.
|
||||
//
|
||||
// This is equivalent to [CursorPosition](1, 1).
|
||||
// corner of the scrolling region. This is equivalent to `CursorPosition(1, 1)`.
|
||||
const CursorHomePosition = "\x1b[H"
|
||||
|
||||
// SetCursorPosition (CUP) returns a sequence for setting the cursor to the
|
||||
|
||||
465
vendor/github.com/charmbracelet/x/ansi/mode.go
generated
vendored
465
vendor/github.com/charmbracelet/x/ansi/mode.go
generated
vendored
@ -108,7 +108,7 @@ func DECRST(modes ...Mode) string {
|
||||
|
||||
func setMode(reset bool, modes ...Mode) (s string) {
|
||||
if len(modes) == 0 {
|
||||
return s
|
||||
return //nolint:nakedret
|
||||
}
|
||||
|
||||
cmd := "h"
|
||||
@ -142,7 +142,7 @@ func setMode(reset bool, modes ...Mode) (s string) {
|
||||
if len(dec) > 0 {
|
||||
s += seq + "?" + strings.Join(dec, ";") + cmd
|
||||
}
|
||||
return s
|
||||
return //nolint:nakedret
|
||||
}
|
||||
|
||||
// RequestMode (DECRQM) returns a sequence to request a mode from the terminal.
|
||||
@ -228,12 +228,12 @@ func (m DECMode) Mode() int {
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/KAM.html
|
||||
const (
|
||||
ModeKeyboardAction = ANSIMode(2)
|
||||
KAM = ModeKeyboardAction
|
||||
KeyboardActionMode = ANSIMode(2)
|
||||
KAM = KeyboardActionMode
|
||||
|
||||
SetModeKeyboardAction = "\x1b[2h"
|
||||
ResetModeKeyboardAction = "\x1b[2l"
|
||||
RequestModeKeyboardAction = "\x1b[2$p"
|
||||
SetKeyboardActionMode = "\x1b[2h"
|
||||
ResetKeyboardActionMode = "\x1b[2l"
|
||||
RequestKeyboardActionMode = "\x1b[2$p"
|
||||
)
|
||||
|
||||
// Insert/Replace Mode (IRM) is a mode that determines whether characters are
|
||||
@ -245,12 +245,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/IRM.html
|
||||
const (
|
||||
ModeInsertReplace = ANSIMode(4)
|
||||
IRM = ModeInsertReplace
|
||||
InsertReplaceMode = ANSIMode(4)
|
||||
IRM = InsertReplaceMode
|
||||
|
||||
SetModeInsertReplace = "\x1b[4h"
|
||||
ResetModeInsertReplace = "\x1b[4l"
|
||||
RequestModeInsertReplace = "\x1b[4$p"
|
||||
SetInsertReplaceMode = "\x1b[4h"
|
||||
ResetInsertReplaceMode = "\x1b[4l"
|
||||
RequestInsertReplaceMode = "\x1b[4$p"
|
||||
)
|
||||
|
||||
// BiDirectional Support Mode (BDSM) is a mode that determines whether the
|
||||
@ -260,12 +260,12 @@ const (
|
||||
//
|
||||
// See ECMA-48 7.2.1.
|
||||
const (
|
||||
ModeBiDirectionalSupport = ANSIMode(8)
|
||||
BDSM = ModeBiDirectionalSupport
|
||||
BiDirectionalSupportMode = ANSIMode(8)
|
||||
BDSM = BiDirectionalSupportMode
|
||||
|
||||
SetModeBiDirectionalSupport = "\x1b[8h"
|
||||
ResetModeBiDirectionalSupport = "\x1b[8l"
|
||||
RequestModeBiDirectionalSupport = "\x1b[8$p"
|
||||
SetBiDirectionalSupportMode = "\x1b[8h"
|
||||
ResetBiDirectionalSupportMode = "\x1b[8l"
|
||||
RequestBiDirectionalSupportMode = "\x1b[8$p"
|
||||
)
|
||||
|
||||
// Send Receive Mode (SRM) or Local Echo Mode is a mode that determines whether
|
||||
@ -274,17 +274,17 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/SRM.html
|
||||
const (
|
||||
ModeSendReceive = ANSIMode(12)
|
||||
ModeLocalEcho = ModeSendReceive
|
||||
SRM = ModeSendReceive
|
||||
SendReceiveMode = ANSIMode(12)
|
||||
LocalEchoMode = SendReceiveMode
|
||||
SRM = SendReceiveMode
|
||||
|
||||
SetModeSendReceive = "\x1b[12h"
|
||||
ResetModeSendReceive = "\x1b[12l"
|
||||
RequestModeSendReceive = "\x1b[12$p"
|
||||
SetSendReceiveMode = "\x1b[12h"
|
||||
ResetSendReceiveMode = "\x1b[12l"
|
||||
RequestSendReceiveMode = "\x1b[12$p"
|
||||
|
||||
SetModeLocalEcho = "\x1b[12h"
|
||||
ResetModeLocalEcho = "\x1b[12l"
|
||||
RequestModeLocalEcho = "\x1b[12$p"
|
||||
SetLocalEchoMode = "\x1b[12h"
|
||||
ResetLocalEchoMode = "\x1b[12l"
|
||||
RequestLocalEchoMode = "\x1b[12$p"
|
||||
)
|
||||
|
||||
// Line Feed/New Line Mode (LNM) is a mode that determines whether the terminal
|
||||
@ -299,12 +299,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/LNM.html
|
||||
const (
|
||||
ModeLineFeedNewLine = ANSIMode(20)
|
||||
LNM = ModeLineFeedNewLine
|
||||
LineFeedNewLineMode = ANSIMode(20)
|
||||
LNM = LineFeedNewLineMode
|
||||
|
||||
SetModeLineFeedNewLine = "\x1b[20h"
|
||||
ResetModeLineFeedNewLine = "\x1b[20l"
|
||||
RequestModeLineFeedNewLine = "\x1b[20$p"
|
||||
SetLineFeedNewLineMode = "\x1b[20h"
|
||||
ResetLineFeedNewLineMode = "\x1b[20l"
|
||||
RequestLineFeedNewLineMode = "\x1b[20$p"
|
||||
)
|
||||
|
||||
// Cursor Keys Mode (DECCKM) is a mode that determines whether the cursor keys
|
||||
@ -312,12 +312,18 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECCKM.html
|
||||
const (
|
||||
ModeCursorKeys = DECMode(1)
|
||||
DECCKM = ModeCursorKeys
|
||||
CursorKeysMode = DECMode(1)
|
||||
DECCKM = CursorKeysMode
|
||||
|
||||
SetModeCursorKeys = "\x1b[?1h"
|
||||
ResetModeCursorKeys = "\x1b[?1l"
|
||||
RequestModeCursorKeys = "\x1b[?1$p"
|
||||
SetCursorKeysMode = "\x1b[?1h"
|
||||
ResetCursorKeysMode = "\x1b[?1l"
|
||||
RequestCursorKeysMode = "\x1b[?1$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetCursorKeysMode] and [ResetCursorKeysMode] instead.
|
||||
const (
|
||||
EnableCursorKeys = "\x1b[?1h" //nolint:revive // grouped constants
|
||||
DisableCursorKeys = "\x1b[?1l"
|
||||
)
|
||||
|
||||
// Origin Mode (DECOM) is a mode that determines whether the cursor moves to the
|
||||
@ -325,12 +331,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECOM.html
|
||||
const (
|
||||
ModeOrigin = DECMode(6)
|
||||
DECOM = ModeOrigin
|
||||
OriginMode = DECMode(6)
|
||||
DECOM = OriginMode
|
||||
|
||||
SetModeOrigin = "\x1b[?6h"
|
||||
ResetModeOrigin = "\x1b[?6l"
|
||||
RequestModeOrigin = "\x1b[?6$p"
|
||||
SetOriginMode = "\x1b[?6h"
|
||||
ResetOriginMode = "\x1b[?6l"
|
||||
RequestOriginMode = "\x1b[?6$p"
|
||||
)
|
||||
|
||||
// Auto Wrap Mode (DECAWM) is a mode that determines whether the cursor wraps
|
||||
@ -338,12 +344,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECAWM.html
|
||||
const (
|
||||
ModeAutoWrap = DECMode(7)
|
||||
DECAWM = ModeAutoWrap
|
||||
AutoWrapMode = DECMode(7)
|
||||
DECAWM = AutoWrapMode
|
||||
|
||||
SetModeAutoWrap = "\x1b[?7h"
|
||||
ResetModeAutoWrap = "\x1b[?7l"
|
||||
RequestModeAutoWrap = "\x1b[?7$p"
|
||||
SetAutoWrapMode = "\x1b[?7h"
|
||||
ResetAutoWrapMode = "\x1b[?7l"
|
||||
RequestAutoWrapMode = "\x1b[?7$p"
|
||||
)
|
||||
|
||||
// X10 Mouse Mode is a mode that determines whether the mouse reports on button
|
||||
@ -358,29 +364,39 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
ModeMouseX10 = DECMode(9)
|
||||
X10MouseMode = DECMode(9)
|
||||
|
||||
SetModeMouseX10 = "\x1b[?9h"
|
||||
ResetModeMouseX10 = "\x1b[?9l"
|
||||
RequestModeMouseX10 = "\x1b[?9$p"
|
||||
SetX10MouseMode = "\x1b[?9h"
|
||||
ResetX10MouseMode = "\x1b[?9l"
|
||||
RequestX10MouseMode = "\x1b[?9$p"
|
||||
)
|
||||
|
||||
// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
|
||||
const (
|
||||
ModeTextCursorEnable = DECMode(25)
|
||||
DECTCEM = ModeTextCursorEnable
|
||||
TextCursorEnableMode = DECMode(25)
|
||||
DECTCEM = TextCursorEnableMode
|
||||
|
||||
SetModeTextCursorEnable = "\x1b[?25h"
|
||||
ResetModeTextCursorEnable = "\x1b[?25l"
|
||||
RequestModeTextCursorEnable = "\x1b[?25$p"
|
||||
SetTextCursorEnableMode = "\x1b[?25h"
|
||||
ResetTextCursorEnableMode = "\x1b[?25l"
|
||||
RequestTextCursorEnableMode = "\x1b[?25$p"
|
||||
)
|
||||
|
||||
// These are aliases for [SetModeTextCursorEnable] and [ResetModeTextCursorEnable].
|
||||
// These are aliases for [SetTextCursorEnableMode] and [ResetTextCursorEnableMode].
|
||||
const (
|
||||
ShowCursor = SetModeTextCursorEnable
|
||||
HideCursor = ResetModeTextCursorEnable
|
||||
ShowCursor = SetTextCursorEnableMode
|
||||
HideCursor = ResetTextCursorEnableMode
|
||||
)
|
||||
|
||||
// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
|
||||
//
|
||||
// Deprecated: use [SetTextCursorEnableMode] and [ResetTextCursorEnableMode] instead.
|
||||
const (
|
||||
CursorEnableMode = DECMode(25)
|
||||
RequestCursorVisibility = "\x1b[?25$p"
|
||||
)
|
||||
|
||||
// Numeric Keypad Mode (DECNKM) is a mode that determines whether the keypad
|
||||
@ -390,12 +406,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECNKM.html
|
||||
const (
|
||||
ModeNumericKeypad = DECMode(66)
|
||||
DECNKM = ModeNumericKeypad
|
||||
NumericKeypadMode = DECMode(66)
|
||||
DECNKM = NumericKeypadMode
|
||||
|
||||
SetModeNumericKeypad = "\x1b[?66h"
|
||||
ResetModeNumericKeypad = "\x1b[?66l"
|
||||
RequestModeNumericKeypad = "\x1b[?66$p"
|
||||
SetNumericKeypadMode = "\x1b[?66h"
|
||||
ResetNumericKeypadMode = "\x1b[?66l"
|
||||
RequestNumericKeypadMode = "\x1b[?66$p"
|
||||
)
|
||||
|
||||
// Backarrow Key Mode (DECBKM) is a mode that determines whether the backspace
|
||||
@ -403,12 +419,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECBKM.html
|
||||
const (
|
||||
ModeBackarrowKey = DECMode(67)
|
||||
DECBKM = ModeBackarrowKey
|
||||
BackarrowKeyMode = DECMode(67)
|
||||
DECBKM = BackarrowKeyMode
|
||||
|
||||
SetModeBackarrowKey = "\x1b[?67h"
|
||||
ResetModeBackarrowKey = "\x1b[?67l"
|
||||
RequestModeBackarrowKey = "\x1b[?67$p"
|
||||
SetBackarrowKeyMode = "\x1b[?67h"
|
||||
ResetBackarrowKeyMode = "\x1b[?67l"
|
||||
RequestBackarrowKeyMode = "\x1b[?67$p"
|
||||
)
|
||||
|
||||
// Left Right Margin Mode (DECLRMM) is a mode that determines whether the left
|
||||
@ -416,33 +432,47 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECLRMM.html
|
||||
const (
|
||||
ModeLeftRightMargin = DECMode(69)
|
||||
DECLRMM = ModeLeftRightMargin
|
||||
LeftRightMarginMode = DECMode(69)
|
||||
DECLRMM = LeftRightMarginMode
|
||||
|
||||
SetModeLeftRightMargin = "\x1b[?69h"
|
||||
ResetModeLeftRightMargin = "\x1b[?69l"
|
||||
RequestModeLeftRightMargin = "\x1b[?69$p"
|
||||
SetLeftRightMarginMode = "\x1b[?69h"
|
||||
ResetLeftRightMarginMode = "\x1b[?69l"
|
||||
RequestLeftRightMarginMode = "\x1b[?69$p"
|
||||
)
|
||||
|
||||
// Normal Mouse Mode is a mode that determines whether the mouse reports on
|
||||
// button presses and releases. It will also report modifier keys, wheel
|
||||
// events, and extra buttons.
|
||||
//
|
||||
// It uses the same encoding as [ModeMouseX10] with a few differences:
|
||||
// It uses the same encoding as [X10MouseMode] with a few differences:
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
ModeMouseNormal = DECMode(1000)
|
||||
NormalMouseMode = DECMode(1000)
|
||||
|
||||
SetModeMouseNormal = "\x1b[?1000h"
|
||||
ResetModeMouseNormal = "\x1b[?1000l"
|
||||
RequestModeMouseNormal = "\x1b[?1000$p"
|
||||
SetNormalMouseMode = "\x1b[?1000h"
|
||||
ResetNormalMouseMode = "\x1b[?1000l"
|
||||
RequestNormalMouseMode = "\x1b[?1000$p"
|
||||
)
|
||||
|
||||
// VT Mouse Tracking is a mode that determines whether the mouse reports on
|
||||
// button press and release.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
//
|
||||
// Deprecated: use [NormalMouseMode] instead.
|
||||
const (
|
||||
MouseMode = DECMode(1000)
|
||||
|
||||
EnableMouse = "\x1b[?1000h"
|
||||
DisableMouse = "\x1b[?1000l"
|
||||
RequestMouse = "\x1b[?1000$p"
|
||||
)
|
||||
|
||||
// Highlight Mouse Tracking is a mode that determines whether the mouse reports
|
||||
// on button presses, releases, and highlighted cells.
|
||||
//
|
||||
// It uses the same encoding as [ModeMouseNormal] with a few differences:
|
||||
// It uses the same encoding as [NormalMouseMode] with a few differences:
|
||||
//
|
||||
// On highlight events, the terminal responds with the following encoding:
|
||||
//
|
||||
@ -451,11 +481,11 @@ const (
|
||||
//
|
||||
// Where the parameters are startx, starty, endx, endy, mousex, and mousey.
|
||||
const (
|
||||
ModeMouseHighlight = DECMode(1001)
|
||||
HighlightMouseMode = DECMode(1001)
|
||||
|
||||
SetModeMouseHighlight = "\x1b[?1001h"
|
||||
ResetModeMouseHighlight = "\x1b[?1001l"
|
||||
RequestModeMouseHighlight = "\x1b[?1001$p"
|
||||
SetHighlightMouseMode = "\x1b[?1001h"
|
||||
ResetHighlightMouseMode = "\x1b[?1001l"
|
||||
RequestHighlightMouseMode = "\x1b[?1001$p"
|
||||
)
|
||||
|
||||
// VT Hilite Mouse Tracking is a mode that determines whether the mouse reports on
|
||||
@ -463,29 +493,65 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
//
|
||||
// Deprecated: use [HighlightMouseMode] instead.
|
||||
const (
|
||||
MouseHiliteMode = DECMode(1001)
|
||||
|
||||
// Button Event Mouse Tracking is essentially the same as [ModeMouseNormal],
|
||||
EnableMouseHilite = "\x1b[?1001h"
|
||||
DisableMouseHilite = "\x1b[?1001l"
|
||||
RequestMouseHilite = "\x1b[?1001$p"
|
||||
)
|
||||
|
||||
// Button Event Mouse Tracking is essentially the same as [NormalMouseMode],
|
||||
// but it also reports button-motion events when a button is pressed.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
ModeMouseButtonEvent = DECMode(1002)
|
||||
ButtonEventMouseMode = DECMode(1002)
|
||||
|
||||
SetModeMouseButtonEvent = "\x1b[?1002h"
|
||||
ResetModeMouseButtonEvent = "\x1b[?1002l"
|
||||
RequestModeMouseButtonEvent = "\x1b[?1002$p"
|
||||
SetButtonEventMouseMode = "\x1b[?1002h"
|
||||
ResetButtonEventMouseMode = "\x1b[?1002l"
|
||||
RequestButtonEventMouseMode = "\x1b[?1002$p"
|
||||
)
|
||||
|
||||
// Any Event Mouse Tracking is the same as [ModeMouseButtonEvent], except that
|
||||
// Cell Motion Mouse Tracking is a mode that determines whether the mouse
|
||||
// reports on button press, release, and motion events.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
//
|
||||
// Deprecated: use [ButtonEventMouseMode] instead.
|
||||
const (
|
||||
MouseCellMotionMode = DECMode(1002)
|
||||
|
||||
EnableMouseCellMotion = "\x1b[?1002h"
|
||||
DisableMouseCellMotion = "\x1b[?1002l"
|
||||
RequestMouseCellMotion = "\x1b[?1002$p"
|
||||
)
|
||||
|
||||
// Any Event Mouse Tracking is the same as [ButtonEventMouseMode], except that
|
||||
// all motion events are reported even if no mouse buttons are pressed.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
ModeMouseAnyEvent = DECMode(1003)
|
||||
AnyEventMouseMode = DECMode(1003)
|
||||
|
||||
SetModeMouseAnyEvent = "\x1b[?1003h"
|
||||
ResetModeMouseAnyEvent = "\x1b[?1003l"
|
||||
RequestModeMouseAnyEvent = "\x1b[?1003$p"
|
||||
SetAnyEventMouseMode = "\x1b[?1003h"
|
||||
ResetAnyEventMouseMode = "\x1b[?1003l"
|
||||
RequestAnyEventMouseMode = "\x1b[?1003$p"
|
||||
)
|
||||
|
||||
// All Mouse Tracking is a mode that determines whether the mouse reports on
|
||||
// button press, release, motion, and highlight events.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
//
|
||||
// Deprecated: use [AnyEventMouseMode] instead.
|
||||
const (
|
||||
MouseAllMotionMode = DECMode(1003)
|
||||
|
||||
EnableMouseAllMotion = "\x1b[?1003h"
|
||||
DisableMouseAllMotion = "\x1b[?1003l"
|
||||
RequestMouseAllMotion = "\x1b[?1003$p"
|
||||
)
|
||||
|
||||
// Focus Event Mode is a mode that determines whether the terminal reports focus
|
||||
@ -498,11 +564,22 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Focus-Tracking
|
||||
const (
|
||||
ModeFocusEvent = DECMode(1004)
|
||||
FocusEventMode = DECMode(1004)
|
||||
|
||||
SetModeFocusEvent = "\x1b[?1004h"
|
||||
ResetModeFocusEvent = "\x1b[?1004l"
|
||||
RequestModeFocusEvent = "\x1b[?1004$p"
|
||||
SetFocusEventMode = "\x1b[?1004h"
|
||||
ResetFocusEventMode = "\x1b[?1004l"
|
||||
RequestFocusEventMode = "\x1b[?1004$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetFocusEventMode], [ResetFocusEventMode], and
|
||||
// [RequestFocusEventMode] instead.
|
||||
// Focus reporting mode constants.
|
||||
const (
|
||||
ReportFocusMode = DECMode(1004) //nolint:revive // grouped constants
|
||||
|
||||
EnableReportFocus = "\x1b[?1004h"
|
||||
DisableReportFocus = "\x1b[?1004l"
|
||||
RequestReportFocus = "\x1b[?1004$p"
|
||||
)
|
||||
|
||||
// SGR Extended Mouse Mode is a mode that changes the mouse tracking encoding
|
||||
@ -512,15 +589,24 @@ const (
|
||||
//
|
||||
// CSI < Cb ; Cx ; Cy M
|
||||
//
|
||||
// Where Cb is the same as [ModeMouseNormal], and Cx and Cy are the x and y.
|
||||
// Where Cb is the same as [NormalMouseMode], and Cx and Cy are the x and y.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
ModeMouseExtSgr = DECMode(1006)
|
||||
SgrExtMouseMode = DECMode(1006)
|
||||
|
||||
SetModeMouseExtSgr = "\x1b[?1006h"
|
||||
ResetModeMouseExtSgr = "\x1b[?1006l"
|
||||
RequestModeMouseExtSgr = "\x1b[?1006$p"
|
||||
SetSgrExtMouseMode = "\x1b[?1006h"
|
||||
ResetSgrExtMouseMode = "\x1b[?1006l"
|
||||
RequestSgrExtMouseMode = "\x1b[?1006$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SgrExtMouseMode] [SetSgrExtMouseMode],
|
||||
// [ResetSgrExtMouseMode], and [RequestSgrExtMouseMode] instead.
|
||||
const (
|
||||
MouseSgrExtMode = DECMode(1006) //nolint:revive // grouped constants
|
||||
EnableMouseSgrExt = "\x1b[?1006h"
|
||||
DisableMouseSgrExt = "\x1b[?1006l"
|
||||
RequestMouseSgrExt = "\x1b[?1006$p"
|
||||
)
|
||||
|
||||
// UTF-8 Extended Mouse Mode is a mode that changes the mouse tracking encoding
|
||||
@ -528,11 +614,11 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
ModeMouseExtUtf8 = DECMode(1005)
|
||||
Utf8ExtMouseMode = DECMode(1005)
|
||||
|
||||
SetModeMouseExtUtf8 = "\x1b[?1005h"
|
||||
ResetModeMouseExtUtf8 = "\x1b[?1005l"
|
||||
RequestModeMouseExtUtf8 = "\x1b[?1005$p"
|
||||
SetUtf8ExtMouseMode = "\x1b[?1005h"
|
||||
ResetUtf8ExtMouseMode = "\x1b[?1005l"
|
||||
RequestUtf8ExtMouseMode = "\x1b[?1005$p"
|
||||
)
|
||||
|
||||
// URXVT Extended Mouse Mode is a mode that changes the mouse tracking encoding
|
||||
@ -540,25 +626,25 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
ModeMouseExtUrxvt = DECMode(1015)
|
||||
UrxvtExtMouseMode = DECMode(1015)
|
||||
|
||||
SetModeMouseExtUrxvt = "\x1b[?1015h"
|
||||
ResetModeMouseExtUrxvt = "\x1b[?1015l"
|
||||
RequestModeMouseExtUrxvt = "\x1b[?1015$p"
|
||||
SetUrxvtExtMouseMode = "\x1b[?1015h"
|
||||
ResetUrxvtExtMouseMode = "\x1b[?1015l"
|
||||
RequestUrxvtExtMouseMode = "\x1b[?1015$p"
|
||||
)
|
||||
|
||||
// SGR Pixel Extended Mouse Mode is a mode that changes the mouse tracking
|
||||
// encoding to use SGR parameters with pixel coordinates.
|
||||
//
|
||||
// This is similar to [ModeMouseExtSgr], but also reports pixel coordinates.
|
||||
// This is similar to [SgrExtMouseMode], but also reports pixel coordinates.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
ModeMouseExtSgrPixel = DECMode(1016)
|
||||
SgrPixelExtMouseMode = DECMode(1016)
|
||||
|
||||
SetModeMouseExtSgrPixel = "\x1b[?1016h"
|
||||
ResetModeMouseExtSgrPixel = "\x1b[?1016l"
|
||||
RequestModeMouseExtSgrPixel = "\x1b[?1016$p"
|
||||
SetSgrPixelExtMouseMode = "\x1b[?1016h"
|
||||
ResetSgrPixelExtMouseMode = "\x1b[?1016l"
|
||||
RequestSgrPixelExtMouseMode = "\x1b[?1016$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Mode is a mode that determines whether the alternate screen
|
||||
@ -567,11 +653,11 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
const (
|
||||
ModeAltScreen = DECMode(1047)
|
||||
AltScreenMode = DECMode(1047)
|
||||
|
||||
SetModeAltScreen = "\x1b[?1047h"
|
||||
ResetModeAltScreen = "\x1b[?1047l"
|
||||
RequestModeAltScreen = "\x1b[?1047$p"
|
||||
SetAltScreenMode = "\x1b[?1047h"
|
||||
ResetAltScreenMode = "\x1b[?1047l"
|
||||
RequestAltScreenMode = "\x1b[?1047$p"
|
||||
)
|
||||
|
||||
// Save Cursor Mode is a mode that saves the cursor position.
|
||||
@ -579,24 +665,42 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
const (
|
||||
ModeSaveCursor = DECMode(1048)
|
||||
SaveCursorMode = DECMode(1048)
|
||||
|
||||
SetModeSaveCursor = "\x1b[?1048h"
|
||||
ResetModeSaveCursor = "\x1b[?1048l"
|
||||
RequestModeSaveCursor = "\x1b[?1048$p"
|
||||
SetSaveCursorMode = "\x1b[?1048h"
|
||||
ResetSaveCursorMode = "\x1b[?1048l"
|
||||
RequestSaveCursorMode = "\x1b[?1048$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Save Cursor Mode is a mode that saves the cursor position as in
|
||||
// [ModeSaveCursor], switches to the alternate screen buffer as in [ModeAltScreen],
|
||||
// [SaveCursorMode], switches to the alternate screen buffer as in [AltScreenMode],
|
||||
// and clears the screen on switch.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
const (
|
||||
ModeAltScreenSaveCursor = DECMode(1049)
|
||||
AltScreenSaveCursorMode = DECMode(1049)
|
||||
|
||||
SetModeAltScreenSaveCursor = "\x1b[?1049h"
|
||||
ResetModeAltScreenSaveCursor = "\x1b[?1049l"
|
||||
RequestModeAltScreenSaveCursor = "\x1b[?1049$p"
|
||||
SetAltScreenSaveCursorMode = "\x1b[?1049h"
|
||||
ResetAltScreenSaveCursorMode = "\x1b[?1049l"
|
||||
RequestAltScreenSaveCursorMode = "\x1b[?1049$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Buffer is a mode that determines whether the alternate screen
|
||||
// buffer is active.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
//
|
||||
// Deprecated: use [AltScreenSaveCursorMode] instead.
|
||||
const (
|
||||
AltScreenBufferMode = DECMode(1049)
|
||||
|
||||
SetAltScreenBufferMode = "\x1b[?1049h"
|
||||
ResetAltScreenBufferMode = "\x1b[?1049l"
|
||||
RequestAltScreenBufferMode = "\x1b[?1049$p"
|
||||
|
||||
EnableAltScreenBuffer = "\x1b[?1049h"
|
||||
DisableAltScreenBuffer = "\x1b[?1049l"
|
||||
RequestAltScreenBuffer = "\x1b[?1049$p"
|
||||
)
|
||||
|
||||
// Bracketed Paste Mode is a mode that determines whether pasted text is
|
||||
@ -605,11 +709,19 @@ const (
|
||||
// See: https://cirw.in/blog/bracketed-paste
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode
|
||||
const (
|
||||
ModeBracketedPaste = DECMode(2004)
|
||||
BracketedPasteMode = DECMode(2004)
|
||||
|
||||
SetModeBracketedPaste = "\x1b[?2004h"
|
||||
ResetModeBracketedPaste = "\x1b[?2004l"
|
||||
RequestModeBracketedPaste = "\x1b[?2004$p"
|
||||
SetBracketedPasteMode = "\x1b[?2004h"
|
||||
ResetBracketedPasteMode = "\x1b[?2004l"
|
||||
RequestBracketedPasteMode = "\x1b[?2004$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetBracketedPasteMode], [ResetBracketedPasteMode], and
|
||||
// [RequestBracketedPasteMode] instead.
|
||||
const (
|
||||
EnableBracketedPaste = "\x1b[?2004h" //nolint:revive // grouped constants
|
||||
DisableBracketedPaste = "\x1b[?2004l"
|
||||
RequestBracketedPaste = "\x1b[?2004$p"
|
||||
)
|
||||
|
||||
// Synchronized Output Mode is a mode that determines whether output is
|
||||
@ -617,11 +729,23 @@ const (
|
||||
//
|
||||
// See: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036
|
||||
const (
|
||||
ModeSynchronizedOutput = DECMode(2026)
|
||||
SynchronizedOutputMode = DECMode(2026)
|
||||
|
||||
SetModeSynchronizedOutput = "\x1b[?2026h"
|
||||
ResetModeSynchronizedOutput = "\x1b[?2026l"
|
||||
RequestModeSynchronizedOutput = "\x1b[?2026$p"
|
||||
SetSynchronizedOutputMode = "\x1b[?2026h"
|
||||
ResetSynchronizedOutputMode = "\x1b[?2026l"
|
||||
RequestSynchronizedOutputMode = "\x1b[?2026$p"
|
||||
)
|
||||
|
||||
// Synchronized Output Mode. See [SynchronizedOutputMode].
|
||||
//
|
||||
// Deprecated: use [SynchronizedOutputMode], [SetSynchronizedOutputMode], and
|
||||
// [ResetSynchronizedOutputMode], and [RequestSynchronizedOutputMode] instead.
|
||||
const (
|
||||
SyncdOutputMode = DECMode(2026)
|
||||
|
||||
EnableSyncdOutput = "\x1b[?2026h"
|
||||
DisableSyncdOutput = "\x1b[?2026l"
|
||||
RequestSyncdOutput = "\x1b[?2026$p"
|
||||
)
|
||||
|
||||
// Unicode Core Mode is a mode that determines whether the terminal should use
|
||||
@ -630,16 +754,41 @@ const (
|
||||
//
|
||||
// See: https://github.com/contour-terminal/terminal-unicode-core
|
||||
const (
|
||||
ModeUnicodeCore = DECMode(2027)
|
||||
UnicodeCoreMode = DECMode(2027)
|
||||
|
||||
SetModeUnicodeCore = "\x1b[?2027h"
|
||||
ResetModeUnicodeCore = "\x1b[?2027l"
|
||||
RequestModeUnicodeCore = "\x1b[?2027$p"
|
||||
SetUnicodeCoreMode = "\x1b[?2027h"
|
||||
ResetUnicodeCoreMode = "\x1b[?2027l"
|
||||
RequestUnicodeCoreMode = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Grapheme Clustering Mode is a mode that determines whether the terminal
|
||||
// should look for grapheme clusters instead of single runes in the rendered
|
||||
// text. This makes the terminal properly render combining characters such as
|
||||
// emojis.
|
||||
//
|
||||
// See: https://github.com/contour-terminal/terminal-unicode-core
|
||||
//
|
||||
// Deprecated: use [GraphemeClusteringMode], [SetUnicodeCoreMode],
|
||||
// [ResetUnicodeCoreMode], and [RequestUnicodeCoreMode] instead.
|
||||
const (
|
||||
GraphemeClusteringMode = DECMode(2027)
|
||||
|
||||
// ModeLightDark is a mode that enables reporting the operating system's color
|
||||
SetGraphemeClusteringMode = "\x1b[?2027h"
|
||||
ResetGraphemeClusteringMode = "\x1b[?2027l"
|
||||
RequestGraphemeClusteringMode = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Grapheme Clustering Mode. See [GraphemeClusteringMode].
|
||||
//
|
||||
// Deprecated: use [SetUnicodeCoreMode], [ResetUnicodeCoreMode], and
|
||||
// [RequestUnicodeCoreMode] instead.
|
||||
const (
|
||||
EnableGraphemeClustering = "\x1b[?2027h"
|
||||
DisableGraphemeClustering = "\x1b[?2027l"
|
||||
RequestGraphemeClustering = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// LightDarkMode is a mode that enables reporting the operating system's color
|
||||
// scheme (light or dark) preference. It reports the color scheme as a [DSR]
|
||||
// and [LightDarkReport] escape sequences encoded as follows:
|
||||
//
|
||||
@ -653,14 +802,14 @@ const (
|
||||
//
|
||||
// See: https://contour-terminal.org/vt-extensions/color-palette-update-notifications/
|
||||
const (
|
||||
ModeLightDark = DECMode(2031)
|
||||
LightDarkMode = DECMode(2031)
|
||||
|
||||
SetModeLightDark = "\x1b[?2031h"
|
||||
ResetModeLightDark = "\x1b[?2031l"
|
||||
RequestModeLightDark = "\x1b[?2031$p"
|
||||
SetLightDarkMode = "\x1b[?2031h"
|
||||
ResetLightDarkMode = "\x1b[?2031l"
|
||||
RequestLightDarkMode = "\x1b[?2031$p"
|
||||
)
|
||||
|
||||
// ModeInBandResize is a mode that reports terminal resize events as escape
|
||||
// InBandResizeMode is a mode that reports terminal resize events as escape
|
||||
// sequences. This is useful for systems that do not support [SIGWINCH] like
|
||||
// Windows.
|
||||
//
|
||||
@ -670,11 +819,11 @@ const (
|
||||
//
|
||||
// See: https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3704bd83
|
||||
const (
|
||||
ModeInBandResize = DECMode(2048)
|
||||
InBandResizeMode = DECMode(2048)
|
||||
|
||||
SetModeInBandResize = "\x1b[?2048h"
|
||||
ResetModeInBandResize = "\x1b[?2048l"
|
||||
RequestModeInBandResize = "\x1b[?2048$p"
|
||||
SetInBandResizeMode = "\x1b[?2048h"
|
||||
ResetInBandResizeMode = "\x1b[?2048l"
|
||||
RequestInBandResizeMode = "\x1b[?2048$p"
|
||||
)
|
||||
|
||||
// Win32Input is a mode that determines whether input is processed by the
|
||||
@ -682,9 +831,17 @@ const (
|
||||
//
|
||||
// See: https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md
|
||||
const (
|
||||
ModeWin32Input = DECMode(9001)
|
||||
Win32InputMode = DECMode(9001)
|
||||
|
||||
SetModeWin32Input = "\x1b[?9001h"
|
||||
ResetModeWin32Input = "\x1b[?9001l"
|
||||
RequestModeWin32Input = "\x1b[?9001$p"
|
||||
SetWin32InputMode = "\x1b[?9001h"
|
||||
ResetWin32InputMode = "\x1b[?9001l"
|
||||
RequestWin32InputMode = "\x1b[?9001$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetWin32InputMode], [ResetWin32InputMode], and
|
||||
// [RequestWin32InputMode] instead.
|
||||
const (
|
||||
EnableWin32Input = "\x1b[?9001h" //nolint:revive // grouped constants
|
||||
DisableWin32Input = "\x1b[?9001l"
|
||||
RequestWin32Input = "\x1b[?9001$p"
|
||||
)
|
||||
|
||||
495
vendor/github.com/charmbracelet/x/ansi/mode_deprecated.go
generated
vendored
495
vendor/github.com/charmbracelet/x/ansi/mode_deprecated.go
generated
vendored
@ -1,495 +0,0 @@
|
||||
package ansi
|
||||
|
||||
// Keyboard Action Mode (KAM) controls locking of the keyboard.
|
||||
//
|
||||
// Deprecated: use [ModeKeyboardAction] instead.
|
||||
const (
|
||||
KeyboardActionMode = ANSIMode(2)
|
||||
|
||||
SetKeyboardActionMode = "\x1b[2h"
|
||||
ResetKeyboardActionMode = "\x1b[2l"
|
||||
RequestKeyboardActionMode = "\x1b[2$p"
|
||||
)
|
||||
|
||||
// Insert/Replace Mode (IRM) determines whether characters are inserted or replaced.
|
||||
//
|
||||
// Deprecated: use [ModeInsertReplace] instead.
|
||||
const (
|
||||
InsertReplaceMode = ANSIMode(4)
|
||||
|
||||
SetInsertReplaceMode = "\x1b[4h"
|
||||
ResetInsertReplaceMode = "\x1b[4l"
|
||||
RequestInsertReplaceMode = "\x1b[4$p"
|
||||
)
|
||||
|
||||
// BiDirectional Support Mode (BDSM) determines whether the terminal supports bidirectional text.
|
||||
//
|
||||
// Deprecated: use [ModeBiDirectionalSupport] instead.
|
||||
const (
|
||||
BiDirectionalSupportMode = ANSIMode(8)
|
||||
|
||||
SetBiDirectionalSupportMode = "\x1b[8h"
|
||||
ResetBiDirectionalSupportMode = "\x1b[8l"
|
||||
RequestBiDirectionalSupportMode = "\x1b[8$p"
|
||||
)
|
||||
|
||||
// Send Receive Mode (SRM) or Local Echo Mode determines whether the terminal echoes characters.
|
||||
//
|
||||
// Deprecated: use [ModeSendReceive] instead.
|
||||
const (
|
||||
SendReceiveMode = ANSIMode(12)
|
||||
LocalEchoMode = SendReceiveMode
|
||||
|
||||
SetSendReceiveMode = "\x1b[12h"
|
||||
ResetSendReceiveMode = "\x1b[12l"
|
||||
RequestSendReceiveMode = "\x1b[12$p"
|
||||
|
||||
SetLocalEchoMode = "\x1b[12h"
|
||||
ResetLocalEchoMode = "\x1b[12l"
|
||||
RequestLocalEchoMode = "\x1b[12$p"
|
||||
)
|
||||
|
||||
// Line Feed/New Line Mode (LNM) determines whether the terminal interprets line feed as new line.
|
||||
//
|
||||
// Deprecated: use [ModeLineFeedNewLine] instead.
|
||||
const (
|
||||
LineFeedNewLineMode = ANSIMode(20)
|
||||
|
||||
SetLineFeedNewLineMode = "\x1b[20h"
|
||||
ResetLineFeedNewLineMode = "\x1b[20l"
|
||||
RequestLineFeedNewLineMode = "\x1b[20$p"
|
||||
)
|
||||
|
||||
// Cursor Keys Mode (DECCKM) determines whether cursor keys send ANSI or application sequences.
|
||||
//
|
||||
// Deprecated: use [ModeCursorKeys] instead.
|
||||
const (
|
||||
CursorKeysMode = DECMode(1)
|
||||
|
||||
SetCursorKeysMode = "\x1b[?1h"
|
||||
ResetCursorKeysMode = "\x1b[?1l"
|
||||
RequestCursorKeysMode = "\x1b[?1$p"
|
||||
)
|
||||
|
||||
// Cursor Keys mode.
|
||||
//
|
||||
// Deprecated: use [SetModeCursorKeys] and [ResetModeCursorKeys] instead.
|
||||
const (
|
||||
EnableCursorKeys = "\x1b[?1h"
|
||||
DisableCursorKeys = "\x1b[?1l"
|
||||
)
|
||||
|
||||
// Origin Mode (DECOM) determines whether the cursor moves to home or margin position.
|
||||
//
|
||||
// Deprecated: use [ModeOrigin] instead.
|
||||
const (
|
||||
OriginMode = DECMode(6)
|
||||
|
||||
SetOriginMode = "\x1b[?6h"
|
||||
ResetOriginMode = "\x1b[?6l"
|
||||
RequestOriginMode = "\x1b[?6$p"
|
||||
)
|
||||
|
||||
// Auto Wrap Mode (DECAWM) determines whether the cursor wraps to the next line.
|
||||
//
|
||||
// Deprecated: use [ModeAutoWrap] instead.
|
||||
const (
|
||||
AutoWrapMode = DECMode(7)
|
||||
|
||||
SetAutoWrapMode = "\x1b[?7h"
|
||||
ResetAutoWrapMode = "\x1b[?7l"
|
||||
RequestAutoWrapMode = "\x1b[?7$p"
|
||||
)
|
||||
|
||||
// X10 Mouse Mode determines whether the mouse reports on button presses.
|
||||
//
|
||||
// Deprecated: use [ModeMouseX10] instead.
|
||||
const (
|
||||
X10MouseMode = DECMode(9)
|
||||
|
||||
SetX10MouseMode = "\x1b[?9h"
|
||||
ResetX10MouseMode = "\x1b[?9l"
|
||||
RequestX10MouseMode = "\x1b[?9$p"
|
||||
)
|
||||
|
||||
// Text Cursor Enable Mode (DECTCEM) shows/hides the cursor.
|
||||
//
|
||||
// Deprecated: use [ModeTextCursorEnable] instead.
|
||||
const (
|
||||
TextCursorEnableMode = DECMode(25)
|
||||
|
||||
SetTextCursorEnableMode = "\x1b[?25h"
|
||||
ResetTextCursorEnableMode = "\x1b[?25l"
|
||||
RequestTextCursorEnableMode = "\x1b[?25$p"
|
||||
)
|
||||
|
||||
// Text Cursor Enable mode.
|
||||
//
|
||||
// Deprecated: use [SetModeTextCursorEnable] and [ResetModeTextCursorEnable] instead.
|
||||
const (
|
||||
CursorEnableMode = DECMode(25)
|
||||
RequestCursorVisibility = "\x1b[?25$p"
|
||||
)
|
||||
|
||||
// Numeric Keypad Mode (DECNKM) determines whether the keypad sends application or numeric sequences.
|
||||
//
|
||||
// Deprecated: use [ModeNumericKeypad] instead.
|
||||
const (
|
||||
NumericKeypadMode = DECMode(66)
|
||||
|
||||
SetNumericKeypadMode = "\x1b[?66h"
|
||||
ResetNumericKeypadMode = "\x1b[?66l"
|
||||
RequestNumericKeypadMode = "\x1b[?66$p"
|
||||
)
|
||||
|
||||
// Backarrow Key Mode (DECBKM) determines whether the backspace key sends backspace or delete.
|
||||
//
|
||||
// Deprecated: use [ModeBackarrowKey] instead.
|
||||
const (
|
||||
BackarrowKeyMode = DECMode(67)
|
||||
|
||||
SetBackarrowKeyMode = "\x1b[?67h"
|
||||
ResetBackarrowKeyMode = "\x1b[?67l"
|
||||
RequestBackarrowKeyMode = "\x1b[?67$p"
|
||||
)
|
||||
|
||||
// Left Right Margin Mode (DECLRMM) determines whether left and right margins can be set.
|
||||
//
|
||||
// Deprecated: use [ModeLeftRightMargin] instead.
|
||||
const (
|
||||
LeftRightMarginMode = DECMode(69)
|
||||
|
||||
SetLeftRightMarginMode = "\x1b[?69h"
|
||||
ResetLeftRightMarginMode = "\x1b[?69l"
|
||||
RequestLeftRightMarginMode = "\x1b[?69$p"
|
||||
)
|
||||
|
||||
// Normal Mouse Mode determines whether the mouse reports on button presses and releases.
|
||||
//
|
||||
// Deprecated: use [ModeMouseNormal] instead.
|
||||
const (
|
||||
NormalMouseMode = DECMode(1000)
|
||||
|
||||
SetNormalMouseMode = "\x1b[?1000h"
|
||||
ResetNormalMouseMode = "\x1b[?1000l"
|
||||
RequestNormalMouseMode = "\x1b[?1000$p"
|
||||
)
|
||||
|
||||
// VT Mouse Tracking mode.
|
||||
//
|
||||
// Deprecated: use [ModeMouseNormal] instead.
|
||||
const (
|
||||
MouseMode = DECMode(1000)
|
||||
|
||||
EnableMouse = "\x1b[?1000h"
|
||||
DisableMouse = "\x1b[?1000l"
|
||||
RequestMouse = "\x1b[?1000$p"
|
||||
)
|
||||
|
||||
// Highlight Mouse Tracking determines whether the mouse reports on button presses and highlighted cells.
|
||||
//
|
||||
// Deprecated: use [ModeMouseHighlight] instead.
|
||||
const (
|
||||
HighlightMouseMode = DECMode(1001)
|
||||
|
||||
SetHighlightMouseMode = "\x1b[?1001h"
|
||||
ResetHighlightMouseMode = "\x1b[?1001l"
|
||||
RequestHighlightMouseMode = "\x1b[?1001$p"
|
||||
)
|
||||
|
||||
// VT Hilite Mouse Tracking mode.
|
||||
//
|
||||
// Deprecated: use [ModeMouseHighlight] instead.
|
||||
const (
|
||||
MouseHiliteMode = DECMode(1001)
|
||||
|
||||
EnableMouseHilite = "\x1b[?1001h"
|
||||
DisableMouseHilite = "\x1b[?1001l"
|
||||
RequestMouseHilite = "\x1b[?1001$p"
|
||||
)
|
||||
|
||||
// Button Event Mouse Tracking reports button-motion events when a button is pressed.
|
||||
//
|
||||
// Deprecated: use [ModeMouseButtonEvent] instead.
|
||||
const (
|
||||
ButtonEventMouseMode = DECMode(1002)
|
||||
|
||||
SetButtonEventMouseMode = "\x1b[?1002h"
|
||||
ResetButtonEventMouseMode = "\x1b[?1002l"
|
||||
RequestButtonEventMouseMode = "\x1b[?1002$p"
|
||||
)
|
||||
|
||||
// Cell Motion Mouse Tracking mode.
|
||||
//
|
||||
// Deprecated: use [ModeMouseButtonEvent] instead.
|
||||
const (
|
||||
MouseCellMotionMode = DECMode(1002)
|
||||
|
||||
EnableMouseCellMotion = "\x1b[?1002h"
|
||||
DisableMouseCellMotion = "\x1b[?1002l"
|
||||
RequestMouseCellMotion = "\x1b[?1002$p"
|
||||
)
|
||||
|
||||
// Any Event Mouse Tracking reports all motion events.
|
||||
//
|
||||
// Deprecated: use [ModeMouseAnyEvent] instead.
|
||||
const (
|
||||
AnyEventMouseMode = DECMode(1003)
|
||||
|
||||
SetAnyEventMouseMode = "\x1b[?1003h"
|
||||
ResetAnyEventMouseMode = "\x1b[?1003l"
|
||||
RequestAnyEventMouseMode = "\x1b[?1003$p"
|
||||
)
|
||||
|
||||
// All Mouse Tracking mode.
|
||||
//
|
||||
// Deprecated: use [ModeMouseAnyEvent] instead.
|
||||
const (
|
||||
MouseAllMotionMode = DECMode(1003)
|
||||
|
||||
EnableMouseAllMotion = "\x1b[?1003h"
|
||||
DisableMouseAllMotion = "\x1b[?1003l"
|
||||
RequestMouseAllMotion = "\x1b[?1003$p"
|
||||
)
|
||||
|
||||
// Focus Event Mode determines whether the terminal reports focus and blur events.
|
||||
//
|
||||
// Deprecated: use [ModeFocusEvent] instead.
|
||||
const (
|
||||
FocusEventMode = DECMode(1004)
|
||||
|
||||
SetFocusEventMode = "\x1b[?1004h"
|
||||
ResetFocusEventMode = "\x1b[?1004l"
|
||||
RequestFocusEventMode = "\x1b[?1004$p"
|
||||
)
|
||||
|
||||
// Focus reporting mode.
|
||||
//
|
||||
// Deprecated: use [SetModeFocusEvent], [ResetModeFocusEvent], and
|
||||
// [RequestModeFocusEvent] instead.
|
||||
const (
|
||||
ReportFocusMode = DECMode(1004)
|
||||
|
||||
EnableReportFocus = "\x1b[?1004h"
|
||||
DisableReportFocus = "\x1b[?1004l"
|
||||
RequestReportFocus = "\x1b[?1004$p"
|
||||
)
|
||||
|
||||
// UTF-8 Extended Mouse Mode changes the mouse tracking encoding to use UTF-8 parameters.
|
||||
//
|
||||
// Deprecated: use [ModeMouseExtUtf8] instead.
|
||||
const (
|
||||
Utf8ExtMouseMode = DECMode(1005)
|
||||
|
||||
SetUtf8ExtMouseMode = "\x1b[?1005h"
|
||||
ResetUtf8ExtMouseMode = "\x1b[?1005l"
|
||||
RequestUtf8ExtMouseMode = "\x1b[?1005$p"
|
||||
)
|
||||
|
||||
// SGR Extended Mouse Mode changes the mouse tracking encoding to use SGR parameters.
|
||||
//
|
||||
// Deprecated: use [ModeMouseExtSgr] instead.
|
||||
const (
|
||||
SgrExtMouseMode = DECMode(1006)
|
||||
|
||||
SetSgrExtMouseMode = "\x1b[?1006h"
|
||||
ResetSgrExtMouseMode = "\x1b[?1006l"
|
||||
RequestSgrExtMouseMode = "\x1b[?1006$p"
|
||||
)
|
||||
|
||||
// Mouse SGR Extended mode.
|
||||
//
|
||||
// Deprecated: use [ModeMouseExtSgr], [SetModeMouseExtSgr],
|
||||
// [ResetModeMouseExtSgr], and [RequestModeMouseExtSgr] instead.
|
||||
const (
|
||||
MouseSgrExtMode = DECMode(1006)
|
||||
EnableMouseSgrExt = "\x1b[?1006h"
|
||||
DisableMouseSgrExt = "\x1b[?1006l"
|
||||
RequestMouseSgrExt = "\x1b[?1006$p"
|
||||
)
|
||||
|
||||
// URXVT Extended Mouse Mode changes the mouse tracking encoding to use an alternate encoding.
|
||||
//
|
||||
// Deprecated: use [ModeMouseUrxvtExt] instead.
|
||||
const (
|
||||
UrxvtExtMouseMode = DECMode(1015)
|
||||
|
||||
SetUrxvtExtMouseMode = "\x1b[?1015h"
|
||||
ResetUrxvtExtMouseMode = "\x1b[?1015l"
|
||||
RequestUrxvtExtMouseMode = "\x1b[?1015$p"
|
||||
)
|
||||
|
||||
// SGR Pixel Extended Mouse Mode changes the mouse tracking encoding to use SGR parameters with pixel coordinates.
|
||||
//
|
||||
// Deprecated: use [ModeMouseExtSgrPixel] instead.
|
||||
const (
|
||||
SgrPixelExtMouseMode = DECMode(1016)
|
||||
|
||||
SetSgrPixelExtMouseMode = "\x1b[?1016h"
|
||||
ResetSgrPixelExtMouseMode = "\x1b[?1016l"
|
||||
RequestSgrPixelExtMouseMode = "\x1b[?1016$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Mode determines whether the alternate screen buffer is active.
|
||||
//
|
||||
// Deprecated: use [ModeAltScreen] instead.
|
||||
const (
|
||||
AltScreenMode = DECMode(1047)
|
||||
|
||||
SetAltScreenMode = "\x1b[?1047h"
|
||||
ResetAltScreenMode = "\x1b[?1047l"
|
||||
RequestAltScreenMode = "\x1b[?1047$p"
|
||||
)
|
||||
|
||||
// Save Cursor Mode saves the cursor position.
|
||||
//
|
||||
// Deprecated: use [ModeSaveCursor] instead.
|
||||
const (
|
||||
SaveCursorMode = DECMode(1048)
|
||||
|
||||
SetSaveCursorMode = "\x1b[?1048h"
|
||||
ResetSaveCursorMode = "\x1b[?1048l"
|
||||
RequestSaveCursorMode = "\x1b[?1048$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Save Cursor Mode saves the cursor position and switches to alternate screen.
|
||||
//
|
||||
// Deprecated: use [ModeAltScreenSaveCursor] instead.
|
||||
const (
|
||||
AltScreenSaveCursorMode = DECMode(1049)
|
||||
|
||||
SetAltScreenSaveCursorMode = "\x1b[?1049h"
|
||||
ResetAltScreenSaveCursorMode = "\x1b[?1049l"
|
||||
RequestAltScreenSaveCursorMode = "\x1b[?1049$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Buffer mode.
|
||||
//
|
||||
// Deprecated: use [ModeAltScreenSaveCursor] instead.
|
||||
const (
|
||||
AltScreenBufferMode = DECMode(1049)
|
||||
|
||||
SetAltScreenBufferMode = "\x1b[?1049h"
|
||||
ResetAltScreenBufferMode = "\x1b[?1049l"
|
||||
RequestAltScreenBufferMode = "\x1b[?1049$p"
|
||||
|
||||
EnableAltScreenBuffer = "\x1b[?1049h"
|
||||
DisableAltScreenBuffer = "\x1b[?1049l"
|
||||
RequestAltScreenBuffer = "\x1b[?1049$p"
|
||||
)
|
||||
|
||||
// Bracketed Paste Mode determines whether pasted text is bracketed with escape sequences.
|
||||
//
|
||||
// Deprecated: use [ModeBracketedPaste] instead.
|
||||
const (
|
||||
BracketedPasteMode = DECMode(2004)
|
||||
|
||||
SetBracketedPasteMode = "\x1b[?2004h"
|
||||
ResetBracketedPasteMode = "\x1b[?2004l"
|
||||
RequestBracketedPasteMode = "\x1b[?2004$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetModeBracketedPaste], [ResetModeBracketedPaste], and
|
||||
// [RequestModeBracketedPaste] instead.
|
||||
const (
|
||||
EnableBracketedPaste = "\x1b[?2004h" //nolint:revive
|
||||
DisableBracketedPaste = "\x1b[?2004l"
|
||||
RequestBracketedPaste = "\x1b[?2004$p"
|
||||
)
|
||||
|
||||
// Synchronized Output Mode determines whether output is synchronized with the terminal.
|
||||
//
|
||||
// Deprecated: use [ModeSynchronizedOutput] instead.
|
||||
const (
|
||||
SynchronizedOutputMode = DECMode(2026)
|
||||
|
||||
SetSynchronizedOutputMode = "\x1b[?2026h"
|
||||
ResetSynchronizedOutputMode = "\x1b[?2026l"
|
||||
RequestSynchronizedOutputMode = "\x1b[?2026$p"
|
||||
)
|
||||
|
||||
// Synchronized output mode.
|
||||
//
|
||||
// Deprecated: use [ModeSynchronizedOutput], [SetModeSynchronizedOutput],
|
||||
// [ResetModeSynchronizedOutput], and [RequestModeSynchronizedOutput] instead.
|
||||
const (
|
||||
SyncdOutputMode = DECMode(2026)
|
||||
|
||||
EnableSyncdOutput = "\x1b[?2026h"
|
||||
DisableSyncdOutput = "\x1b[?2026l"
|
||||
RequestSyncdOutput = "\x1b[?2026$p"
|
||||
)
|
||||
|
||||
// Unicode Core Mode determines whether the terminal uses Unicode grapheme clustering.
|
||||
//
|
||||
// Deprecated: use [ModeUnicodeCore] instead.
|
||||
const (
|
||||
UnicodeCoreMode = DECMode(2027)
|
||||
|
||||
SetUnicodeCoreMode = "\x1b[?2027h"
|
||||
ResetUnicodeCoreMode = "\x1b[?2027l"
|
||||
RequestUnicodeCoreMode = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Grapheme Clustering Mode determines whether the terminal looks for grapheme clusters.
|
||||
//
|
||||
// Deprecated: use [ModeUnicodeCore], [SetModeUnicodeCore],
|
||||
// [ResetModeUnicodeCore], and [RequestModeUnicodeCore] instead.
|
||||
const (
|
||||
GraphemeClusteringMode = DECMode(2027)
|
||||
|
||||
SetGraphemeClusteringMode = "\x1b[?2027h"
|
||||
ResetGraphemeClusteringMode = "\x1b[?2027l"
|
||||
RequestGraphemeClusteringMode = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Unicode Core mode.
|
||||
//
|
||||
// Deprecated: use [SetModeUnicodeCore], [ResetModeUnicodeCore], and
|
||||
// [RequestModeUnicodeCore] instead.
|
||||
const (
|
||||
EnableGraphemeClustering = "\x1b[?2027h"
|
||||
DisableGraphemeClustering = "\x1b[?2027l"
|
||||
RequestGraphemeClustering = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Light Dark Mode enables reporting the operating system's color scheme preference.
|
||||
//
|
||||
// Deprecated: use [ModeLightDark] instead.
|
||||
const (
|
||||
LightDarkMode = DECMode(2031)
|
||||
|
||||
SetLightDarkMode = "\x1b[?2031h"
|
||||
ResetLightDarkMode = "\x1b[?2031l"
|
||||
RequestLightDarkMode = "\x1b[?2031$p"
|
||||
)
|
||||
|
||||
// In Band Resize Mode reports terminal resize events as escape sequences.
|
||||
//
|
||||
// Deprecated: use [ModeInBandResize] instead.
|
||||
const (
|
||||
InBandResizeMode = DECMode(2048)
|
||||
|
||||
SetInBandResizeMode = "\x1b[?2048h"
|
||||
ResetInBandResizeMode = "\x1b[?2048l"
|
||||
RequestInBandResizeMode = "\x1b[?2048$p"
|
||||
)
|
||||
|
||||
// Win32Input determines whether input is processed by the Win32 console and Conpty.
|
||||
//
|
||||
// Deprecated: use [ModeWin32Input] instead.
|
||||
const (
|
||||
Win32InputMode = DECMode(9001)
|
||||
|
||||
SetWin32InputMode = "\x1b[?9001h"
|
||||
ResetWin32InputMode = "\x1b[?9001l"
|
||||
RequestWin32InputMode = "\x1b[?9001$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetModeWin32Input], [ResetModeWin32Input], and
|
||||
// [RequestModeWin32Input] instead.
|
||||
const (
|
||||
EnableWin32Input = "\x1b[?9001h" //nolint:revive
|
||||
DisableWin32Input = "\x1b[?9001l"
|
||||
RequestWin32Input = "\x1b[?9001$p"
|
||||
)
|
||||
2
vendor/github.com/charmbracelet/x/ansi/mouse.go
generated
vendored
2
vendor/github.com/charmbracelet/x/ansi/mouse.go
generated
vendored
@ -134,7 +134,7 @@ func EncodeMouseButton(b MouseButton, motion, shift, alt, ctrl bool) (m byte) {
|
||||
m |= bitMotion
|
||||
}
|
||||
|
||||
return m
|
||||
return //nolint:nakedret
|
||||
}
|
||||
|
||||
// x10Offset is the offset for X10 mouse events.
|
||||
|
||||
19
vendor/github.com/charmbracelet/x/ansi/notification.go
generated
vendored
19
vendor/github.com/charmbracelet/x/ansi/notification.go
generated
vendored
@ -1,10 +1,5 @@
|
||||
package ansi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Notify sends a desktop notification using iTerm's OSC 9.
|
||||
//
|
||||
// OSC 9 ; Mc ST
|
||||
@ -16,17 +11,3 @@ import (
|
||||
func Notify(s string) string {
|
||||
return "\x1b]9;" + s + "\x07"
|
||||
}
|
||||
|
||||
// DesktopNotification sends a desktop notification based on the extensible OSC
|
||||
// 99 escape code.
|
||||
//
|
||||
// OSC 99 ; <metadata> ; <payload> ST
|
||||
// OSC 99 ; <metadata> ; <payload> BEL
|
||||
//
|
||||
// Where <metadata> is a colon-separated list of key-value pairs, and
|
||||
// <payload> is the notification body.
|
||||
//
|
||||
// See: https://sw.kovidgoyal.net/kitty/desktop-notifications/
|
||||
func DesktopNotification(payload string, metadata ...string) string {
|
||||
return fmt.Sprintf("\x1b]99;%s;%s\x07", strings.Join(metadata, ":"), payload)
|
||||
}
|
||||
|
||||
33
vendor/github.com/charmbracelet/x/ansi/parser_decode.go
generated
vendored
33
vendor/github.com/charmbracelet/x/ansi/parser_decode.go
generated
vendored
@ -4,9 +4,8 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/clipperhouse/displaywidth"
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// State represents the state of the ANSI escape sequence parser used by
|
||||
@ -177,7 +176,10 @@ func decodeSequence[T string | []byte](m Method, b T, state State, p *Parser) (s
|
||||
}
|
||||
|
||||
if utf8.RuneStart(c) {
|
||||
seq, width = FirstGraphemeCluster(b, m)
|
||||
seq, _, width, _ = FirstGraphemeCluster(b, -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(seq))
|
||||
}
|
||||
i += len(seq)
|
||||
return b[:i], width, i, NormalState
|
||||
}
|
||||
@ -432,22 +434,17 @@ func HasEscPrefix[T string | []byte](b T) bool {
|
||||
return len(b) > 0 && b[0] == ESC
|
||||
}
|
||||
|
||||
// FirstGraphemeCluster returns the first grapheme cluster in the given string
|
||||
// or byte slice, and its monospace display width.
|
||||
func FirstGraphemeCluster[T string | []byte](b T, m Method) (T, int) {
|
||||
// FirstGraphemeCluster returns the first grapheme cluster in the given string or byte slice.
|
||||
// This is a syntactic sugar function that wraps
|
||||
// uniseg.FirstGraphemeClusterInString and uniseg.FirstGraphemeCluster.
|
||||
func FirstGraphemeCluster[T string | []byte](b T, state int) (T, T, int, int) {
|
||||
switch b := any(b).(type) {
|
||||
case string:
|
||||
cluster := graphemes.FromString(b).First()
|
||||
if m == WcWidth {
|
||||
return T(cluster), runewidth.StringWidth(cluster)
|
||||
}
|
||||
return T(cluster), displaywidth.String(cluster)
|
||||
cluster, rest, width, newState := uniseg.FirstGraphemeClusterInString(b, state)
|
||||
return T(cluster), T(rest), width, newState
|
||||
case []byte:
|
||||
cluster := graphemes.FromBytes(b).First()
|
||||
if m == WcWidth {
|
||||
return T(cluster), runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
return T(cluster), displaywidth.Bytes(cluster)
|
||||
cluster, rest, width, newState := uniseg.FirstGraphemeCluster(b, state)
|
||||
return T(cluster), T(rest), width, newState
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
@ -493,7 +490,7 @@ func Command(prefix, inter, final byte) (c int) {
|
||||
c = int(final)
|
||||
c |= int(prefix) << parser.PrefixShift
|
||||
c |= int(inter) << parser.IntermedShift
|
||||
return c
|
||||
return
|
||||
}
|
||||
|
||||
// Param represents a sequence parameter. Sequence parameters with
|
||||
@ -523,5 +520,5 @@ func Parameter(p int, hasMore bool) (s int) {
|
||||
if hasMore {
|
||||
s |= parser.HasMoreFlag
|
||||
}
|
||||
return s
|
||||
return
|
||||
}
|
||||
|
||||
110
vendor/github.com/charmbracelet/x/ansi/sgr.go
generated
vendored
110
vendor/github.com/charmbracelet/x/ansi/sgr.go
generated
vendored
@ -21,59 +21,59 @@ func SGR(ps ...Attr) string {
|
||||
}
|
||||
|
||||
var attrStrings = map[int]string{
|
||||
AttrReset: attrReset,
|
||||
AttrBold: attrBold,
|
||||
AttrFaint: attrFaint,
|
||||
AttrItalic: attrItalic,
|
||||
AttrUnderline: attrUnderline,
|
||||
AttrBlink: attrBlink,
|
||||
AttrRapidBlink: attrRapidBlink,
|
||||
AttrReverse: attrReverse,
|
||||
AttrConceal: attrConceal,
|
||||
AttrStrikethrough: attrStrikethrough,
|
||||
AttrNormalIntensity: attrNormalIntensity,
|
||||
AttrNoItalic: attrNoItalic,
|
||||
AttrNoUnderline: attrNoUnderline,
|
||||
AttrNoBlink: attrNoBlink,
|
||||
AttrNoReverse: attrNoReverse,
|
||||
AttrNoConceal: attrNoConceal,
|
||||
AttrNoStrikethrough: attrNoStrikethrough,
|
||||
AttrBlackForegroundColor: attrBlackForegroundColor,
|
||||
AttrRedForegroundColor: attrRedForegroundColor,
|
||||
AttrGreenForegroundColor: attrGreenForegroundColor,
|
||||
AttrYellowForegroundColor: attrYellowForegroundColor,
|
||||
AttrBlueForegroundColor: attrBlueForegroundColor,
|
||||
AttrMagentaForegroundColor: attrMagentaForegroundColor,
|
||||
AttrCyanForegroundColor: attrCyanForegroundColor,
|
||||
AttrWhiteForegroundColor: attrWhiteForegroundColor,
|
||||
AttrExtendedForegroundColor: attrExtendedForegroundColor,
|
||||
AttrDefaultForegroundColor: attrDefaultForegroundColor,
|
||||
AttrBlackBackgroundColor: attrBlackBackgroundColor,
|
||||
AttrRedBackgroundColor: attrRedBackgroundColor,
|
||||
AttrGreenBackgroundColor: attrGreenBackgroundColor,
|
||||
AttrYellowBackgroundColor: attrYellowBackgroundColor,
|
||||
AttrBlueBackgroundColor: attrBlueBackgroundColor,
|
||||
AttrMagentaBackgroundColor: attrMagentaBackgroundColor,
|
||||
AttrCyanBackgroundColor: attrCyanBackgroundColor,
|
||||
AttrWhiteBackgroundColor: attrWhiteBackgroundColor,
|
||||
AttrExtendedBackgroundColor: attrExtendedBackgroundColor,
|
||||
AttrDefaultBackgroundColor: attrDefaultBackgroundColor,
|
||||
AttrExtendedUnderlineColor: attrExtendedUnderlineColor,
|
||||
AttrDefaultUnderlineColor: attrDefaultUnderlineColor,
|
||||
AttrBrightBlackForegroundColor: attrBrightBlackForegroundColor,
|
||||
AttrBrightRedForegroundColor: attrBrightRedForegroundColor,
|
||||
AttrBrightGreenForegroundColor: attrBrightGreenForegroundColor,
|
||||
AttrBrightYellowForegroundColor: attrBrightYellowForegroundColor,
|
||||
AttrBrightBlueForegroundColor: attrBrightBlueForegroundColor,
|
||||
AttrBrightMagentaForegroundColor: attrBrightMagentaForegroundColor,
|
||||
AttrBrightCyanForegroundColor: attrBrightCyanForegroundColor,
|
||||
AttrBrightWhiteForegroundColor: attrBrightWhiteForegroundColor,
|
||||
AttrBrightBlackBackgroundColor: attrBrightBlackBackgroundColor,
|
||||
AttrBrightRedBackgroundColor: attrBrightRedBackgroundColor,
|
||||
AttrBrightGreenBackgroundColor: attrBrightGreenBackgroundColor,
|
||||
AttrBrightYellowBackgroundColor: attrBrightYellowBackgroundColor,
|
||||
AttrBrightBlueBackgroundColor: attrBrightBlueBackgroundColor,
|
||||
AttrBrightMagentaBackgroundColor: attrBrightMagentaBackgroundColor,
|
||||
AttrBrightCyanBackgroundColor: attrBrightCyanBackgroundColor,
|
||||
AttrBrightWhiteBackgroundColor: attrBrightWhiteBackgroundColor,
|
||||
ResetAttr: resetAttr,
|
||||
BoldAttr: boldAttr,
|
||||
FaintAttr: faintAttr,
|
||||
ItalicAttr: italicAttr,
|
||||
UnderlineAttr: underlineAttr,
|
||||
SlowBlinkAttr: slowBlinkAttr,
|
||||
RapidBlinkAttr: rapidBlinkAttr,
|
||||
ReverseAttr: reverseAttr,
|
||||
ConcealAttr: concealAttr,
|
||||
StrikethroughAttr: strikethroughAttr,
|
||||
NormalIntensityAttr: normalIntensityAttr,
|
||||
NoItalicAttr: noItalicAttr,
|
||||
NoUnderlineAttr: noUnderlineAttr,
|
||||
NoBlinkAttr: noBlinkAttr,
|
||||
NoReverseAttr: noReverseAttr,
|
||||
NoConcealAttr: noConcealAttr,
|
||||
NoStrikethroughAttr: noStrikethroughAttr,
|
||||
BlackForegroundColorAttr: blackForegroundColorAttr,
|
||||
RedForegroundColorAttr: redForegroundColorAttr,
|
||||
GreenForegroundColorAttr: greenForegroundColorAttr,
|
||||
YellowForegroundColorAttr: yellowForegroundColorAttr,
|
||||
BlueForegroundColorAttr: blueForegroundColorAttr,
|
||||
MagentaForegroundColorAttr: magentaForegroundColorAttr,
|
||||
CyanForegroundColorAttr: cyanForegroundColorAttr,
|
||||
WhiteForegroundColorAttr: whiteForegroundColorAttr,
|
||||
ExtendedForegroundColorAttr: extendedForegroundColorAttr,
|
||||
DefaultForegroundColorAttr: defaultForegroundColorAttr,
|
||||
BlackBackgroundColorAttr: blackBackgroundColorAttr,
|
||||
RedBackgroundColorAttr: redBackgroundColorAttr,
|
||||
GreenBackgroundColorAttr: greenBackgroundColorAttr,
|
||||
YellowBackgroundColorAttr: yellowBackgroundColorAttr,
|
||||
BlueBackgroundColorAttr: blueBackgroundColorAttr,
|
||||
MagentaBackgroundColorAttr: magentaBackgroundColorAttr,
|
||||
CyanBackgroundColorAttr: cyanBackgroundColorAttr,
|
||||
WhiteBackgroundColorAttr: whiteBackgroundColorAttr,
|
||||
ExtendedBackgroundColorAttr: extendedBackgroundColorAttr,
|
||||
DefaultBackgroundColorAttr: defaultBackgroundColorAttr,
|
||||
ExtendedUnderlineColorAttr: extendedUnderlineColorAttr,
|
||||
DefaultUnderlineColorAttr: defaultUnderlineColorAttr,
|
||||
BrightBlackForegroundColorAttr: brightBlackForegroundColorAttr,
|
||||
BrightRedForegroundColorAttr: brightRedForegroundColorAttr,
|
||||
BrightGreenForegroundColorAttr: brightGreenForegroundColorAttr,
|
||||
BrightYellowForegroundColorAttr: brightYellowForegroundColorAttr,
|
||||
BrightBlueForegroundColorAttr: brightBlueForegroundColorAttr,
|
||||
BrightMagentaForegroundColorAttr: brightMagentaForegroundColorAttr,
|
||||
BrightCyanForegroundColorAttr: brightCyanForegroundColorAttr,
|
||||
BrightWhiteForegroundColorAttr: brightWhiteForegroundColorAttr,
|
||||
BrightBlackBackgroundColorAttr: brightBlackBackgroundColorAttr,
|
||||
BrightRedBackgroundColorAttr: brightRedBackgroundColorAttr,
|
||||
BrightGreenBackgroundColorAttr: brightGreenBackgroundColorAttr,
|
||||
BrightYellowBackgroundColorAttr: brightYellowBackgroundColorAttr,
|
||||
BrightBlueBackgroundColorAttr: brightBlueBackgroundColorAttr,
|
||||
BrightMagentaBackgroundColorAttr: brightMagentaBackgroundColorAttr,
|
||||
BrightCyanBackgroundColorAttr: brightCyanBackgroundColorAttr,
|
||||
BrightWhiteBackgroundColorAttr: brightWhiteBackgroundColorAttr,
|
||||
}
|
||||
|
||||
630
vendor/github.com/charmbracelet/x/ansi/style.go
generated
vendored
630
vendor/github.com/charmbracelet/x/ansi/style.go
generated
vendored
@ -17,9 +17,7 @@ type Attr = int
|
||||
// Style represents an ANSI SGR (Select Graphic Rendition) style.
|
||||
type Style []string
|
||||
|
||||
// NewStyle returns a new style with the given attributes. Attributes are SGR
|
||||
// (Select Graphic Rendition) codes that control text formatting like bold,
|
||||
// italic, colors, etc.
|
||||
// NewStyle returns a new style with the given attributes.
|
||||
func NewStyle(attrs ...Attr) Style {
|
||||
if len(attrs) == 0 {
|
||||
return Style{}
|
||||
@ -48,8 +46,7 @@ func (s Style) String() string {
|
||||
return "\x1b[" + strings.Join(s, ";") + "m"
|
||||
}
|
||||
|
||||
// Styled returns a styled string with the given style applied. The style is
|
||||
// applied at the beginning and reset at the end of the string.
|
||||
// Styled returns a styled string with the given style applied.
|
||||
func (s Style) Styled(str string) string {
|
||||
if len(s) == 0 {
|
||||
return str
|
||||
@ -57,446 +54,309 @@ func (s Style) Styled(str string) string {
|
||||
return s.String() + str + ResetStyle
|
||||
}
|
||||
|
||||
// Reset appends the reset style attribute to the style. This resets all
|
||||
// formatting attributes to their defaults.
|
||||
// Reset appends the reset style attribute to the style.
|
||||
func (s Style) Reset() Style {
|
||||
return append(s, attrReset)
|
||||
return append(s, resetAttr)
|
||||
}
|
||||
|
||||
// Bold appends the bold or normal intensity style attribute to the style.
|
||||
// You can use [Style.Normal] to reset to normal intensity.
|
||||
// Bold appends the bold style attribute to the style.
|
||||
func (s Style) Bold() Style {
|
||||
return append(s, attrBold)
|
||||
return append(s, boldAttr)
|
||||
}
|
||||
|
||||
// Faint appends the faint or normal intensity style attribute to the style.
|
||||
// You can use [Style.Normal] to reset to normal intensity.
|
||||
// Faint appends the faint style attribute to the style.
|
||||
func (s Style) Faint() Style {
|
||||
return append(s, attrFaint)
|
||||
return append(s, faintAttr)
|
||||
}
|
||||
|
||||
// Italic appends the italic or no italic style attribute to the style.
|
||||
// When v is true, text is rendered in italic. When false, italic is disabled.
|
||||
func (s Style) Italic(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrItalic)
|
||||
}
|
||||
return append(s, attrNoItalic)
|
||||
// Italic appends the italic style attribute to the style.
|
||||
func (s Style) Italic() Style {
|
||||
return append(s, italicAttr)
|
||||
}
|
||||
|
||||
// Underline appends the underline or no underline style attribute to the style.
|
||||
// When v is true, text is underlined. When false, underline is disabled.
|
||||
func (s Style) Underline(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrUnderline)
|
||||
}
|
||||
return append(s, attrNoUnderline)
|
||||
// Underline appends the underline style attribute to the style.
|
||||
func (s Style) Underline() Style {
|
||||
return append(s, underlineAttr)
|
||||
}
|
||||
|
||||
// UnderlineStyle appends the underline style attribute to the style.
|
||||
// Supports various underline styles including single, double, curly, dotted,
|
||||
// and dashed.
|
||||
func (s Style) UnderlineStyle(u Underline) Style {
|
||||
func (s Style) UnderlineStyle(u UnderlineStyle) Style {
|
||||
switch u {
|
||||
case UnderlineNone:
|
||||
return s.Underline(false)
|
||||
case UnderlineSingle:
|
||||
return s.Underline(true)
|
||||
case UnderlineDouble:
|
||||
return append(s, underlineDouble)
|
||||
case UnderlineCurly:
|
||||
return append(s, underlineCurly)
|
||||
case UnderlineDotted:
|
||||
return append(s, underlineDotted)
|
||||
case UnderlineDashed:
|
||||
return append(s, underlineDashed)
|
||||
case NoUnderlineStyle:
|
||||
return s.NoUnderline()
|
||||
case SingleUnderlineStyle:
|
||||
return s.Underline()
|
||||
case DoubleUnderlineStyle:
|
||||
return append(s, doubleUnderlineStyle)
|
||||
case CurlyUnderlineStyle:
|
||||
return append(s, curlyUnderlineStyle)
|
||||
case DottedUnderlineStyle:
|
||||
return append(s, dottedUnderlineStyle)
|
||||
case DashedUnderlineStyle:
|
||||
return append(s, dashedUnderlineStyle)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Blink appends the slow blink or no blink style attribute to the style.
|
||||
// When v is true, text blinks slowly (less than 150 per minute). When false,
|
||||
// blinking is disabled.
|
||||
func (s Style) Blink(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrBlink)
|
||||
}
|
||||
return append(s, attrNoBlink)
|
||||
// DoubleUnderline appends the double underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(DoubleUnderlineStyle).
|
||||
func (s Style) DoubleUnderline() Style {
|
||||
return s.UnderlineStyle(DoubleUnderlineStyle)
|
||||
}
|
||||
|
||||
// RapidBlink appends the rapid blink or no blink style attribute to the style.
|
||||
// When v is true, text blinks rapidly (150+ per minute). When false, blinking
|
||||
// is disabled.
|
||||
//
|
||||
// Note that this is not widely supported in terminal emulators.
|
||||
func (s Style) RapidBlink(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrRapidBlink)
|
||||
}
|
||||
return append(s, attrNoBlink)
|
||||
// CurlyUnderline appends the curly underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(CurlyUnderlineStyle).
|
||||
func (s Style) CurlyUnderline() Style {
|
||||
return s.UnderlineStyle(CurlyUnderlineStyle)
|
||||
}
|
||||
|
||||
// Reverse appends the reverse or no reverse style attribute to the style.
|
||||
// When v is true, foreground and background colors are swapped. When false,
|
||||
// reverse video is disabled.
|
||||
func (s Style) Reverse(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrReverse)
|
||||
}
|
||||
return append(s, attrNoReverse)
|
||||
// DottedUnderline appends the dotted underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(DottedUnderlineStyle).
|
||||
func (s Style) DottedUnderline() Style {
|
||||
return s.UnderlineStyle(DottedUnderlineStyle)
|
||||
}
|
||||
|
||||
// Conceal appends the conceal or no conceal style attribute to the style.
|
||||
// When v is true, text is hidden/concealed. When false, concealment is
|
||||
// disabled.
|
||||
func (s Style) Conceal(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrConceal)
|
||||
}
|
||||
return append(s, attrNoConceal)
|
||||
// DashedUnderline appends the dashed underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(DashedUnderlineStyle).
|
||||
func (s Style) DashedUnderline() Style {
|
||||
return s.UnderlineStyle(DashedUnderlineStyle)
|
||||
}
|
||||
|
||||
// Strikethrough appends the strikethrough or no strikethrough style attribute
|
||||
// to the style. When v is true, text is rendered with a horizontal line through
|
||||
// it. When false, strikethrough is disabled.
|
||||
func (s Style) Strikethrough(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrStrikethrough)
|
||||
}
|
||||
return append(s, attrNoStrikethrough)
|
||||
// SlowBlink appends the slow blink style attribute to the style.
|
||||
func (s Style) SlowBlink() Style {
|
||||
return append(s, slowBlinkAttr)
|
||||
}
|
||||
|
||||
// Normal appends the normal intensity style attribute to the style. This
|
||||
// resets [Style.Bold] and [Style.Faint] attributes.
|
||||
func (s Style) Normal() Style {
|
||||
return append(s, attrNormalIntensity)
|
||||
// RapidBlink appends the rapid blink style attribute to the style.
|
||||
func (s Style) RapidBlink() Style {
|
||||
return append(s, rapidBlinkAttr)
|
||||
}
|
||||
|
||||
// Reverse appends the reverse style attribute to the style.
|
||||
func (s Style) Reverse() Style {
|
||||
return append(s, reverseAttr)
|
||||
}
|
||||
|
||||
// Conceal appends the conceal style attribute to the style.
|
||||
func (s Style) Conceal() Style {
|
||||
return append(s, concealAttr)
|
||||
}
|
||||
|
||||
// Strikethrough appends the strikethrough style attribute to the style.
|
||||
func (s Style) Strikethrough() Style {
|
||||
return append(s, strikethroughAttr)
|
||||
}
|
||||
|
||||
// NormalIntensity appends the normal intensity style attribute to the style.
|
||||
func (s Style) NormalIntensity() Style {
|
||||
return append(s, normalIntensityAttr)
|
||||
}
|
||||
|
||||
// NoItalic appends the no italic style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Italic](false) instead.
|
||||
func (s Style) NoItalic() Style {
|
||||
return append(s, attrNoItalic)
|
||||
return append(s, noItalicAttr)
|
||||
}
|
||||
|
||||
// NoUnderline appends the no underline style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Underline](false) instead.
|
||||
func (s Style) NoUnderline() Style {
|
||||
return append(s, attrNoUnderline)
|
||||
return append(s, noUnderlineAttr)
|
||||
}
|
||||
|
||||
// NoBlink appends the no blink style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Blink](false) or [Style.RapidBlink](false) instead.
|
||||
func (s Style) NoBlink() Style {
|
||||
return append(s, attrNoBlink)
|
||||
return append(s, noBlinkAttr)
|
||||
}
|
||||
|
||||
// NoReverse appends the no reverse style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Reverse](false) instead.
|
||||
func (s Style) NoReverse() Style {
|
||||
return append(s, attrNoReverse)
|
||||
return append(s, noReverseAttr)
|
||||
}
|
||||
|
||||
// NoConceal appends the no conceal style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Conceal](false) instead.
|
||||
func (s Style) NoConceal() Style {
|
||||
return append(s, attrNoConceal)
|
||||
return append(s, noConcealAttr)
|
||||
}
|
||||
|
||||
// NoStrikethrough appends the no strikethrough style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Strikethrough](false) instead.
|
||||
func (s Style) NoStrikethrough() Style {
|
||||
return append(s, attrNoStrikethrough)
|
||||
return append(s, noStrikethroughAttr)
|
||||
}
|
||||
|
||||
// DefaultForegroundColor appends the default foreground color style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.ForegroundColor](nil) instead.
|
||||
func (s Style) DefaultForegroundColor() Style {
|
||||
return append(s, attrDefaultForegroundColor)
|
||||
return append(s, defaultForegroundColorAttr)
|
||||
}
|
||||
|
||||
// DefaultBackgroundColor appends the default background color style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.BackgroundColor](nil) instead.
|
||||
func (s Style) DefaultBackgroundColor() Style {
|
||||
return append(s, attrDefaultBackgroundColor)
|
||||
return append(s, defaultBackgroundColorAttr)
|
||||
}
|
||||
|
||||
// DefaultUnderlineColor appends the default underline color style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.UnderlineColor](nil) instead.
|
||||
func (s Style) DefaultUnderlineColor() Style {
|
||||
return append(s, attrDefaultUnderlineColor)
|
||||
return append(s, defaultUnderlineColorAttr)
|
||||
}
|
||||
|
||||
// ForegroundColor appends the foreground color style attribute to the style.
|
||||
// If c is nil, the default foreground color is used. Supports [BasicColor],
|
||||
// [IndexedColor] (256-color), and [color.Color] (24-bit RGB).
|
||||
func (s Style) ForegroundColor(c Color) Style {
|
||||
if c == nil {
|
||||
return append(s, attrDefaultForegroundColor)
|
||||
}
|
||||
return append(s, foregroundColorString(c))
|
||||
}
|
||||
|
||||
// BackgroundColor appends the background color style attribute to the style.
|
||||
// If c is nil, the default background color is used. Supports [BasicColor],
|
||||
// [IndexedColor] (256-color), and [color.Color] (24-bit RGB).
|
||||
func (s Style) BackgroundColor(c Color) Style {
|
||||
if c == nil {
|
||||
return append(s, attrDefaultBackgroundColor)
|
||||
}
|
||||
return append(s, backgroundColorString(c))
|
||||
}
|
||||
|
||||
// UnderlineColor appends the underline color style attribute to the style.
|
||||
// If c is nil, the default underline color is used. Supports [BasicColor],
|
||||
// [IndexedColor] (256-color), and [color.Color] (24-bit RGB).
|
||||
func (s Style) UnderlineColor(c Color) Style {
|
||||
if c == nil {
|
||||
return append(s, attrDefaultUnderlineColor)
|
||||
}
|
||||
return append(s, underlineColorString(c))
|
||||
}
|
||||
|
||||
// Underline represents an ANSI SGR (Select Graphic Rendition) underline style.
|
||||
type Underline = byte
|
||||
|
||||
// UnderlineStyle represents an ANSI SGR (Select Graphic Rendition) underline
|
||||
// style.
|
||||
//
|
||||
// Deprecated: use [Underline] instead.
|
||||
type UnderlineStyle = byte
|
||||
|
||||
const (
|
||||
underlineDouble = "4:2"
|
||||
underlineCurly = "4:3"
|
||||
underlineDotted = "4:4"
|
||||
underlineDashed = "4:5"
|
||||
doubleUnderlineStyle = "4:2"
|
||||
curlyUnderlineStyle = "4:3"
|
||||
dottedUnderlineStyle = "4:4"
|
||||
dashedUnderlineStyle = "4:5"
|
||||
)
|
||||
|
||||
// Underline styles constants.
|
||||
const (
|
||||
UnderlineNone Underline = iota
|
||||
UnderlineSingle
|
||||
UnderlineDouble
|
||||
UnderlineCurly
|
||||
UnderlineDotted
|
||||
UnderlineDashed
|
||||
)
|
||||
|
||||
// Underline styles constants.
|
||||
//
|
||||
// Deprecated: use [UnderlineNone], [UnderlineSingle], etc. instead.
|
||||
const (
|
||||
NoUnderlineStyle Underline = iota
|
||||
// NoUnderlineStyle is the default underline style.
|
||||
NoUnderlineStyle UnderlineStyle = iota
|
||||
// SingleUnderlineStyle is a single underline style.
|
||||
SingleUnderlineStyle
|
||||
// DoubleUnderlineStyle is a double underline style.
|
||||
DoubleUnderlineStyle
|
||||
// CurlyUnderlineStyle is a curly underline style.
|
||||
CurlyUnderlineStyle
|
||||
// DottedUnderlineStyle is a dotted underline style.
|
||||
DottedUnderlineStyle
|
||||
// DashedUnderlineStyle is a dashed underline style.
|
||||
DashedUnderlineStyle
|
||||
)
|
||||
|
||||
// Underline styles constants.
|
||||
//
|
||||
// Deprecated: use [UnderlineNone], [UnderlineSingle], etc. instead.
|
||||
const (
|
||||
UnderlineStyleNone Underline = iota
|
||||
UnderlineStyleSingle
|
||||
UnderlineStyleDouble
|
||||
UnderlineStyleCurly
|
||||
UnderlineStyleDotted
|
||||
UnderlineStyleDashed
|
||||
)
|
||||
|
||||
// SGR (Select Graphic Rendition) style attributes.
|
||||
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
|
||||
const (
|
||||
AttrReset Attr = 0
|
||||
AttrBold Attr = 1
|
||||
AttrFaint Attr = 2
|
||||
AttrItalic Attr = 3
|
||||
AttrUnderline Attr = 4
|
||||
AttrBlink Attr = 5
|
||||
AttrRapidBlink Attr = 6
|
||||
AttrReverse Attr = 7
|
||||
AttrConceal Attr = 8
|
||||
AttrStrikethrough Attr = 9
|
||||
AttrNormalIntensity Attr = 22
|
||||
AttrNoItalic Attr = 23
|
||||
AttrNoUnderline Attr = 24
|
||||
AttrNoBlink Attr = 25
|
||||
AttrNoReverse Attr = 27
|
||||
AttrNoConceal Attr = 28
|
||||
AttrNoStrikethrough Attr = 29
|
||||
AttrBlackForegroundColor Attr = 30
|
||||
AttrRedForegroundColor Attr = 31
|
||||
AttrGreenForegroundColor Attr = 32
|
||||
AttrYellowForegroundColor Attr = 33
|
||||
AttrBlueForegroundColor Attr = 34
|
||||
AttrMagentaForegroundColor Attr = 35
|
||||
AttrCyanForegroundColor Attr = 36
|
||||
AttrWhiteForegroundColor Attr = 37
|
||||
AttrExtendedForegroundColor Attr = 38
|
||||
AttrDefaultForegroundColor Attr = 39
|
||||
AttrBlackBackgroundColor Attr = 40
|
||||
AttrRedBackgroundColor Attr = 41
|
||||
AttrGreenBackgroundColor Attr = 42
|
||||
AttrYellowBackgroundColor Attr = 43
|
||||
AttrBlueBackgroundColor Attr = 44
|
||||
AttrMagentaBackgroundColor Attr = 45
|
||||
AttrCyanBackgroundColor Attr = 46
|
||||
AttrWhiteBackgroundColor Attr = 47
|
||||
AttrExtendedBackgroundColor Attr = 48
|
||||
AttrDefaultBackgroundColor Attr = 49
|
||||
AttrExtendedUnderlineColor Attr = 58
|
||||
AttrDefaultUnderlineColor Attr = 59
|
||||
AttrBrightBlackForegroundColor Attr = 90
|
||||
AttrBrightRedForegroundColor Attr = 91
|
||||
AttrBrightGreenForegroundColor Attr = 92
|
||||
AttrBrightYellowForegroundColor Attr = 93
|
||||
AttrBrightBlueForegroundColor Attr = 94
|
||||
AttrBrightMagentaForegroundColor Attr = 95
|
||||
AttrBrightCyanForegroundColor Attr = 96
|
||||
AttrBrightWhiteForegroundColor Attr = 97
|
||||
AttrBrightBlackBackgroundColor Attr = 100
|
||||
AttrBrightRedBackgroundColor Attr = 101
|
||||
AttrBrightGreenBackgroundColor Attr = 102
|
||||
AttrBrightYellowBackgroundColor Attr = 103
|
||||
AttrBrightBlueBackgroundColor Attr = 104
|
||||
AttrBrightMagentaBackgroundColor Attr = 105
|
||||
AttrBrightCyanBackgroundColor Attr = 106
|
||||
AttrBrightWhiteBackgroundColor Attr = 107
|
||||
ResetAttr Attr = 0
|
||||
BoldAttr Attr = 1
|
||||
FaintAttr Attr = 2
|
||||
ItalicAttr Attr = 3
|
||||
UnderlineAttr Attr = 4
|
||||
SlowBlinkAttr Attr = 5
|
||||
RapidBlinkAttr Attr = 6
|
||||
ReverseAttr Attr = 7
|
||||
ConcealAttr Attr = 8
|
||||
StrikethroughAttr Attr = 9
|
||||
NormalIntensityAttr Attr = 22
|
||||
NoItalicAttr Attr = 23
|
||||
NoUnderlineAttr Attr = 24
|
||||
NoBlinkAttr Attr = 25
|
||||
NoReverseAttr Attr = 27
|
||||
NoConcealAttr Attr = 28
|
||||
NoStrikethroughAttr Attr = 29
|
||||
BlackForegroundColorAttr Attr = 30
|
||||
RedForegroundColorAttr Attr = 31
|
||||
GreenForegroundColorAttr Attr = 32
|
||||
YellowForegroundColorAttr Attr = 33
|
||||
BlueForegroundColorAttr Attr = 34
|
||||
MagentaForegroundColorAttr Attr = 35
|
||||
CyanForegroundColorAttr Attr = 36
|
||||
WhiteForegroundColorAttr Attr = 37
|
||||
ExtendedForegroundColorAttr Attr = 38
|
||||
DefaultForegroundColorAttr Attr = 39
|
||||
BlackBackgroundColorAttr Attr = 40
|
||||
RedBackgroundColorAttr Attr = 41
|
||||
GreenBackgroundColorAttr Attr = 42
|
||||
YellowBackgroundColorAttr Attr = 43
|
||||
BlueBackgroundColorAttr Attr = 44
|
||||
MagentaBackgroundColorAttr Attr = 45
|
||||
CyanBackgroundColorAttr Attr = 46
|
||||
WhiteBackgroundColorAttr Attr = 47
|
||||
ExtendedBackgroundColorAttr Attr = 48
|
||||
DefaultBackgroundColorAttr Attr = 49
|
||||
ExtendedUnderlineColorAttr Attr = 58
|
||||
DefaultUnderlineColorAttr Attr = 59
|
||||
BrightBlackForegroundColorAttr Attr = 90
|
||||
BrightRedForegroundColorAttr Attr = 91
|
||||
BrightGreenForegroundColorAttr Attr = 92
|
||||
BrightYellowForegroundColorAttr Attr = 93
|
||||
BrightBlueForegroundColorAttr Attr = 94
|
||||
BrightMagentaForegroundColorAttr Attr = 95
|
||||
BrightCyanForegroundColorAttr Attr = 96
|
||||
BrightWhiteForegroundColorAttr Attr = 97
|
||||
BrightBlackBackgroundColorAttr Attr = 100
|
||||
BrightRedBackgroundColorAttr Attr = 101
|
||||
BrightGreenBackgroundColorAttr Attr = 102
|
||||
BrightYellowBackgroundColorAttr Attr = 103
|
||||
BrightBlueBackgroundColorAttr Attr = 104
|
||||
BrightMagentaBackgroundColorAttr Attr = 105
|
||||
BrightCyanBackgroundColorAttr Attr = 106
|
||||
BrightWhiteBackgroundColorAttr Attr = 107
|
||||
|
||||
AttrRGBColorIntroducer Attr = 2
|
||||
AttrExtendedColorIntroducer Attr = 5
|
||||
)
|
||||
|
||||
// SGR (Select Graphic Rendition) style attributes.
|
||||
//
|
||||
// Deprecated: use Attr* constants instead.
|
||||
const (
|
||||
ResetAttr = AttrReset
|
||||
BoldAttr = AttrBold
|
||||
FaintAttr = AttrFaint
|
||||
ItalicAttr = AttrItalic
|
||||
UnderlineAttr = AttrUnderline
|
||||
SlowBlinkAttr = AttrBlink
|
||||
RapidBlinkAttr = AttrRapidBlink
|
||||
ReverseAttr = AttrReverse
|
||||
ConcealAttr = AttrConceal
|
||||
StrikethroughAttr = AttrStrikethrough
|
||||
NormalIntensityAttr = AttrNormalIntensity
|
||||
NoItalicAttr = AttrNoItalic
|
||||
NoUnderlineAttr = AttrNoUnderline
|
||||
NoBlinkAttr = AttrNoBlink
|
||||
NoReverseAttr = AttrNoReverse
|
||||
NoConcealAttr = AttrNoConceal
|
||||
NoStrikethroughAttr = AttrNoStrikethrough
|
||||
BlackForegroundColorAttr = AttrBlackForegroundColor
|
||||
RedForegroundColorAttr = AttrRedForegroundColor
|
||||
GreenForegroundColorAttr = AttrGreenForegroundColor
|
||||
YellowForegroundColorAttr = AttrYellowForegroundColor
|
||||
BlueForegroundColorAttr = AttrBlueForegroundColor
|
||||
MagentaForegroundColorAttr = AttrMagentaForegroundColor
|
||||
CyanForegroundColorAttr = AttrCyanForegroundColor
|
||||
WhiteForegroundColorAttr = AttrWhiteForegroundColor
|
||||
ExtendedForegroundColorAttr = AttrExtendedForegroundColor
|
||||
DefaultForegroundColorAttr = AttrDefaultForegroundColor
|
||||
BlackBackgroundColorAttr = AttrBlackBackgroundColor
|
||||
RedBackgroundColorAttr = AttrRedBackgroundColor
|
||||
GreenBackgroundColorAttr = AttrGreenBackgroundColor
|
||||
YellowBackgroundColorAttr = AttrYellowBackgroundColor
|
||||
BlueBackgroundColorAttr = AttrBlueBackgroundColor
|
||||
MagentaBackgroundColorAttr = AttrMagentaBackgroundColor
|
||||
CyanBackgroundColorAttr = AttrCyanBackgroundColor
|
||||
WhiteBackgroundColorAttr = AttrWhiteBackgroundColor
|
||||
ExtendedBackgroundColorAttr = AttrExtendedBackgroundColor
|
||||
DefaultBackgroundColorAttr = AttrDefaultBackgroundColor
|
||||
ExtendedUnderlineColorAttr = AttrExtendedUnderlineColor
|
||||
DefaultUnderlineColorAttr = AttrDefaultUnderlineColor
|
||||
BrightBlackForegroundColorAttr = AttrBrightBlackForegroundColor
|
||||
BrightRedForegroundColorAttr = AttrBrightRedForegroundColor
|
||||
BrightGreenForegroundColorAttr = AttrBrightGreenForegroundColor
|
||||
BrightYellowForegroundColorAttr = AttrBrightYellowForegroundColor
|
||||
BrightBlueForegroundColorAttr = AttrBrightBlueForegroundColor
|
||||
BrightMagentaForegroundColorAttr = AttrBrightMagentaForegroundColor
|
||||
BrightCyanForegroundColorAttr = AttrBrightCyanForegroundColor
|
||||
BrightWhiteForegroundColorAttr = AttrBrightWhiteForegroundColor
|
||||
BrightBlackBackgroundColorAttr = AttrBrightBlackBackgroundColor
|
||||
BrightRedBackgroundColorAttr = AttrBrightRedBackgroundColor
|
||||
BrightGreenBackgroundColorAttr = AttrBrightGreenBackgroundColor
|
||||
BrightYellowBackgroundColorAttr = AttrBrightYellowBackgroundColor
|
||||
BrightBlueBackgroundColorAttr = AttrBrightBlueBackgroundColor
|
||||
BrightMagentaBackgroundColorAttr = AttrBrightMagentaBackgroundColor
|
||||
BrightCyanBackgroundColorAttr = AttrBrightCyanBackgroundColor
|
||||
BrightWhiteBackgroundColorAttr = AttrBrightWhiteBackgroundColor
|
||||
RGBColorIntroducerAttr = AttrRGBColorIntroducer
|
||||
ExtendedColorIntroducerAttr = AttrExtendedColorIntroducer
|
||||
RGBColorIntroducerAttr Attr = 2
|
||||
ExtendedColorIntroducerAttr Attr = 5
|
||||
)
|
||||
|
||||
const (
|
||||
attrReset = "0"
|
||||
attrBold = "1"
|
||||
attrFaint = "2"
|
||||
attrItalic = "3"
|
||||
attrUnderline = "4"
|
||||
attrBlink = "5"
|
||||
attrRapidBlink = "6"
|
||||
attrReverse = "7"
|
||||
attrConceal = "8"
|
||||
attrStrikethrough = "9"
|
||||
attrNormalIntensity = "22"
|
||||
attrNoItalic = "23"
|
||||
attrNoUnderline = "24"
|
||||
attrNoBlink = "25"
|
||||
attrNoReverse = "27"
|
||||
attrNoConceal = "28"
|
||||
attrNoStrikethrough = "29"
|
||||
attrBlackForegroundColor = "30"
|
||||
attrRedForegroundColor = "31"
|
||||
attrGreenForegroundColor = "32"
|
||||
attrYellowForegroundColor = "33"
|
||||
attrBlueForegroundColor = "34"
|
||||
attrMagentaForegroundColor = "35"
|
||||
attrCyanForegroundColor = "36"
|
||||
attrWhiteForegroundColor = "37"
|
||||
attrExtendedForegroundColor = "38"
|
||||
attrDefaultForegroundColor = "39"
|
||||
attrBlackBackgroundColor = "40"
|
||||
attrRedBackgroundColor = "41"
|
||||
attrGreenBackgroundColor = "42"
|
||||
attrYellowBackgroundColor = "43"
|
||||
attrBlueBackgroundColor = "44"
|
||||
attrMagentaBackgroundColor = "45"
|
||||
attrCyanBackgroundColor = "46"
|
||||
attrWhiteBackgroundColor = "47"
|
||||
attrExtendedBackgroundColor = "48"
|
||||
attrDefaultBackgroundColor = "49"
|
||||
attrExtendedUnderlineColor = "58"
|
||||
attrDefaultUnderlineColor = "59"
|
||||
attrBrightBlackForegroundColor = "90"
|
||||
attrBrightRedForegroundColor = "91"
|
||||
attrBrightGreenForegroundColor = "92"
|
||||
attrBrightYellowForegroundColor = "93"
|
||||
attrBrightBlueForegroundColor = "94"
|
||||
attrBrightMagentaForegroundColor = "95"
|
||||
attrBrightCyanForegroundColor = "96"
|
||||
attrBrightWhiteForegroundColor = "97"
|
||||
attrBrightBlackBackgroundColor = "100"
|
||||
attrBrightRedBackgroundColor = "101"
|
||||
attrBrightGreenBackgroundColor = "102"
|
||||
attrBrightYellowBackgroundColor = "103"
|
||||
attrBrightBlueBackgroundColor = "104"
|
||||
attrBrightMagentaBackgroundColor = "105"
|
||||
attrBrightCyanBackgroundColor = "106"
|
||||
attrBrightWhiteBackgroundColor = "107"
|
||||
resetAttr = "0"
|
||||
boldAttr = "1"
|
||||
faintAttr = "2"
|
||||
italicAttr = "3"
|
||||
underlineAttr = "4"
|
||||
slowBlinkAttr = "5"
|
||||
rapidBlinkAttr = "6"
|
||||
reverseAttr = "7"
|
||||
concealAttr = "8"
|
||||
strikethroughAttr = "9"
|
||||
normalIntensityAttr = "22"
|
||||
noItalicAttr = "23"
|
||||
noUnderlineAttr = "24"
|
||||
noBlinkAttr = "25"
|
||||
noReverseAttr = "27"
|
||||
noConcealAttr = "28"
|
||||
noStrikethroughAttr = "29"
|
||||
blackForegroundColorAttr = "30"
|
||||
redForegroundColorAttr = "31"
|
||||
greenForegroundColorAttr = "32"
|
||||
yellowForegroundColorAttr = "33"
|
||||
blueForegroundColorAttr = "34"
|
||||
magentaForegroundColorAttr = "35"
|
||||
cyanForegroundColorAttr = "36"
|
||||
whiteForegroundColorAttr = "37"
|
||||
extendedForegroundColorAttr = "38"
|
||||
defaultForegroundColorAttr = "39"
|
||||
blackBackgroundColorAttr = "40"
|
||||
redBackgroundColorAttr = "41"
|
||||
greenBackgroundColorAttr = "42"
|
||||
yellowBackgroundColorAttr = "43"
|
||||
blueBackgroundColorAttr = "44"
|
||||
magentaBackgroundColorAttr = "45"
|
||||
cyanBackgroundColorAttr = "46"
|
||||
whiteBackgroundColorAttr = "47"
|
||||
extendedBackgroundColorAttr = "48"
|
||||
defaultBackgroundColorAttr = "49"
|
||||
extendedUnderlineColorAttr = "58"
|
||||
defaultUnderlineColorAttr = "59"
|
||||
brightBlackForegroundColorAttr = "90"
|
||||
brightRedForegroundColorAttr = "91"
|
||||
brightGreenForegroundColorAttr = "92"
|
||||
brightYellowForegroundColorAttr = "93"
|
||||
brightBlueForegroundColorAttr = "94"
|
||||
brightMagentaForegroundColorAttr = "95"
|
||||
brightCyanForegroundColorAttr = "96"
|
||||
brightWhiteForegroundColorAttr = "97"
|
||||
brightBlackBackgroundColorAttr = "100"
|
||||
brightRedBackgroundColorAttr = "101"
|
||||
brightGreenBackgroundColorAttr = "102"
|
||||
brightYellowBackgroundColorAttr = "103"
|
||||
brightBlueBackgroundColorAttr = "104"
|
||||
brightMagentaBackgroundColorAttr = "105"
|
||||
brightCyanBackgroundColorAttr = "106"
|
||||
brightWhiteBackgroundColorAttr = "107"
|
||||
)
|
||||
|
||||
// foregroundColorString returns the style SGR attribute for the given
|
||||
@ -504,44 +364,42 @@ const (
|
||||
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
|
||||
func foregroundColorString(c Color) string {
|
||||
switch c := c.(type) {
|
||||
case nil:
|
||||
return attrDefaultForegroundColor
|
||||
case BasicColor:
|
||||
// 3-bit or 4-bit ANSI foreground
|
||||
// "3<n>" or "9<n>" where n is the color number from 0 to 7
|
||||
switch c {
|
||||
case Black:
|
||||
return attrBlackForegroundColor
|
||||
return blackForegroundColorAttr
|
||||
case Red:
|
||||
return attrRedForegroundColor
|
||||
return redForegroundColorAttr
|
||||
case Green:
|
||||
return attrGreenForegroundColor
|
||||
return greenForegroundColorAttr
|
||||
case Yellow:
|
||||
return attrYellowForegroundColor
|
||||
return yellowForegroundColorAttr
|
||||
case Blue:
|
||||
return attrBlueForegroundColor
|
||||
return blueForegroundColorAttr
|
||||
case Magenta:
|
||||
return attrMagentaForegroundColor
|
||||
return magentaForegroundColorAttr
|
||||
case Cyan:
|
||||
return attrCyanForegroundColor
|
||||
return cyanForegroundColorAttr
|
||||
case White:
|
||||
return attrWhiteForegroundColor
|
||||
return whiteForegroundColorAttr
|
||||
case BrightBlack:
|
||||
return attrBrightBlackForegroundColor
|
||||
return brightBlackForegroundColorAttr
|
||||
case BrightRed:
|
||||
return attrBrightRedForegroundColor
|
||||
return brightRedForegroundColorAttr
|
||||
case BrightGreen:
|
||||
return attrBrightGreenForegroundColor
|
||||
return brightGreenForegroundColorAttr
|
||||
case BrightYellow:
|
||||
return attrBrightYellowForegroundColor
|
||||
return brightYellowForegroundColorAttr
|
||||
case BrightBlue:
|
||||
return attrBrightBlueForegroundColor
|
||||
return brightBlueForegroundColorAttr
|
||||
case BrightMagenta:
|
||||
return attrBrightMagentaForegroundColor
|
||||
return brightMagentaForegroundColorAttr
|
||||
case BrightCyan:
|
||||
return attrBrightCyanForegroundColor
|
||||
return brightCyanForegroundColorAttr
|
||||
case BrightWhite:
|
||||
return attrBrightWhiteForegroundColor
|
||||
return brightWhiteForegroundColorAttr
|
||||
}
|
||||
case ExtendedColor:
|
||||
// 256-color ANSI foreground
|
||||
@ -556,7 +414,7 @@ func foregroundColorString(c Color) string {
|
||||
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
|
||||
strconv.FormatUint(uint64(shift(b)), 10)
|
||||
}
|
||||
return attrDefaultForegroundColor
|
||||
return defaultForegroundColorAttr
|
||||
}
|
||||
|
||||
// backgroundColorString returns the style SGR attribute for the given
|
||||
@ -564,44 +422,42 @@ func foregroundColorString(c Color) string {
|
||||
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
|
||||
func backgroundColorString(c Color) string {
|
||||
switch c := c.(type) {
|
||||
case nil:
|
||||
return attrDefaultBackgroundColor
|
||||
case BasicColor:
|
||||
// 3-bit or 4-bit ANSI foreground
|
||||
// "4<n>" or "10<n>" where n is the color number from 0 to 7
|
||||
switch c {
|
||||
case Black:
|
||||
return attrBlackBackgroundColor
|
||||
return blackBackgroundColorAttr
|
||||
case Red:
|
||||
return attrRedBackgroundColor
|
||||
return redBackgroundColorAttr
|
||||
case Green:
|
||||
return attrGreenBackgroundColor
|
||||
return greenBackgroundColorAttr
|
||||
case Yellow:
|
||||
return attrYellowBackgroundColor
|
||||
return yellowBackgroundColorAttr
|
||||
case Blue:
|
||||
return attrBlueBackgroundColor
|
||||
return blueBackgroundColorAttr
|
||||
case Magenta:
|
||||
return attrMagentaBackgroundColor
|
||||
return magentaBackgroundColorAttr
|
||||
case Cyan:
|
||||
return attrCyanBackgroundColor
|
||||
return cyanBackgroundColorAttr
|
||||
case White:
|
||||
return attrWhiteBackgroundColor
|
||||
return whiteBackgroundColorAttr
|
||||
case BrightBlack:
|
||||
return attrBrightBlackBackgroundColor
|
||||
return brightBlackBackgroundColorAttr
|
||||
case BrightRed:
|
||||
return attrBrightRedBackgroundColor
|
||||
return brightRedBackgroundColorAttr
|
||||
case BrightGreen:
|
||||
return attrBrightGreenBackgroundColor
|
||||
return brightGreenBackgroundColorAttr
|
||||
case BrightYellow:
|
||||
return attrBrightYellowBackgroundColor
|
||||
return brightYellowBackgroundColorAttr
|
||||
case BrightBlue:
|
||||
return attrBrightBlueBackgroundColor
|
||||
return brightBlueBackgroundColorAttr
|
||||
case BrightMagenta:
|
||||
return attrBrightMagentaBackgroundColor
|
||||
return brightMagentaBackgroundColorAttr
|
||||
case BrightCyan:
|
||||
return attrBrightCyanBackgroundColor
|
||||
return brightCyanBackgroundColorAttr
|
||||
case BrightWhite:
|
||||
return attrBrightWhiteBackgroundColor
|
||||
return brightWhiteBackgroundColorAttr
|
||||
}
|
||||
case ExtendedColor:
|
||||
// 256-color ANSI foreground
|
||||
@ -616,7 +472,7 @@ func backgroundColorString(c Color) string {
|
||||
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
|
||||
strconv.FormatUint(uint64(shift(b)), 10)
|
||||
}
|
||||
return attrDefaultBackgroundColor
|
||||
return defaultBackgroundColorAttr
|
||||
}
|
||||
|
||||
// underlineColorString returns the style SGR attribute for the given underline
|
||||
@ -624,8 +480,6 @@ func backgroundColorString(c Color) string {
|
||||
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
|
||||
func underlineColorString(c Color) string {
|
||||
switch c := c.(type) {
|
||||
case nil:
|
||||
return attrDefaultUnderlineColor
|
||||
// NOTE: we can't use 3-bit and 4-bit ANSI color codes with underline
|
||||
// color, use 256-color instead.
|
||||
//
|
||||
@ -644,7 +498,7 @@ func underlineColorString(c Color) string {
|
||||
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
|
||||
strconv.FormatUint(uint64(shift(b)), 10)
|
||||
}
|
||||
return attrDefaultUnderlineColor
|
||||
return defaultUnderlineColorAttr
|
||||
}
|
||||
|
||||
// ReadStyleColor decodes a color from a slice of parameters. It returns the
|
||||
@ -672,7 +526,7 @@ func underlineColorString(c Color) string {
|
||||
// 2. Support ignoring and omitting the color space id (second parameter) with respect to RGB colors
|
||||
// 3. Support ignoring and omitting the 6th parameter with respect to RGB and CMY colors
|
||||
// 4. Support reading RGBA colors
|
||||
func ReadStyleColor(params Params, co *color.Color) int {
|
||||
func ReadStyleColor(params Params, co *color.Color) (n int) {
|
||||
if len(params) < 2 { // Need at least SGR type and color type
|
||||
return 0
|
||||
}
|
||||
@ -681,7 +535,7 @@ func ReadStyleColor(params Params, co *color.Color) int {
|
||||
s := params[0]
|
||||
p := params[1]
|
||||
colorType := p.Param(0)
|
||||
n := 2
|
||||
n = 2
|
||||
|
||||
paramsfn := func() (p1, p2, p3, p4 int) {
|
||||
// Where should we start reading the color?
|
||||
@ -740,7 +594,7 @@ func ReadStyleColor(params Params, co *color.Color) int {
|
||||
B: uint8(b), //nolint:gosec
|
||||
A: 0xff,
|
||||
}
|
||||
return n
|
||||
return //nolint:nakedret
|
||||
|
||||
case 3: // CMY direct color
|
||||
if len(params) < 5 {
|
||||
@ -758,7 +612,7 @@ func ReadStyleColor(params Params, co *color.Color) int {
|
||||
Y: uint8(y), //nolint:gosec
|
||||
K: 0,
|
||||
}
|
||||
return n
|
||||
return //nolint:nakedret
|
||||
|
||||
case 4: // CMYK direct color
|
||||
if len(params) < 6 {
|
||||
@ -776,7 +630,7 @@ func ReadStyleColor(params Params, co *color.Color) int {
|
||||
Y: uint8(y), //nolint:gosec
|
||||
K: uint8(k), //nolint:gosec
|
||||
}
|
||||
return n
|
||||
return //nolint:nakedret
|
||||
|
||||
case 5: // indexed color
|
||||
if len(params) < 3 {
|
||||
@ -811,7 +665,7 @@ func ReadStyleColor(params Params, co *color.Color) int {
|
||||
B: uint8(b), //nolint:gosec
|
||||
A: uint8(a), //nolint:gosec
|
||||
}
|
||||
return n
|
||||
return //nolint:nakedret
|
||||
|
||||
default:
|
||||
return 0
|
||||
|
||||
57
vendor/github.com/charmbracelet/x/ansi/truncate.go
generated
vendored
57
vendor/github.com/charmbracelet/x/ansi/truncate.go
generated
vendored
@ -1,11 +1,11 @@
|
||||
package ansi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"bytes"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/clipperhouse/displaywidth"
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// Cut the string, without adding any prefix or tail strings. This function is
|
||||
@ -74,11 +74,12 @@ func truncate(m Method, s string, length int, tail string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
var cluster string
|
||||
var buf strings.Builder
|
||||
var cluster []byte
|
||||
var buf bytes.Buffer
|
||||
curWidth := 0
|
||||
ignoring := false
|
||||
pstate := parser.GroundState // initial state
|
||||
b := []byte(s)
|
||||
i := 0
|
||||
|
||||
// Here we iterate over the bytes of the string and collect printable
|
||||
@ -87,12 +88,16 @@ func truncate(m Method, s string, length int, tail string) string {
|
||||
//
|
||||
// Once we reach the given length, we start ignoring characters and only
|
||||
// collect ANSI escape codes until we reach the end of string.
|
||||
for i < len(s) {
|
||||
state, action := parser.Table.Transition(pstate, s[i])
|
||||
for i < len(b) {
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
if state == parser.Utf8State {
|
||||
// This action happens when we transition to the Utf8State.
|
||||
var width int
|
||||
cluster, width = FirstGraphemeCluster(s[i:], m)
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
|
||||
// increment the index by the length of the cluster
|
||||
i += len(cluster)
|
||||
curWidth += width
|
||||
@ -113,7 +118,7 @@ func truncate(m Method, s string, length int, tail string) string {
|
||||
continue
|
||||
}
|
||||
|
||||
buf.WriteString(cluster)
|
||||
buf.Write(cluster)
|
||||
|
||||
// Done collecting, now we're back in the ground state.
|
||||
pstate = parser.GroundState
|
||||
@ -147,7 +152,7 @@ func truncate(m Method, s string, length int, tail string) string {
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
buf.WriteByte(s[i])
|
||||
buf.WriteByte(b[i])
|
||||
i++
|
||||
}
|
||||
|
||||
@ -188,23 +193,27 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
var cluster string
|
||||
var buf strings.Builder
|
||||
var cluster []byte
|
||||
var buf bytes.Buffer
|
||||
curWidth := 0
|
||||
ignoring := true
|
||||
pstate := parser.GroundState
|
||||
b := []byte(s)
|
||||
i := 0
|
||||
|
||||
for i < len(s) {
|
||||
for i < len(b) {
|
||||
if !ignoring {
|
||||
buf.WriteString(s[i:])
|
||||
buf.Write(b[i:])
|
||||
break
|
||||
}
|
||||
|
||||
state, action := parser.Table.Transition(pstate, s[i])
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
if state == parser.Utf8State {
|
||||
var width int
|
||||
cluster, width = FirstGraphemeCluster(s[i:], m)
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
|
||||
i += len(cluster)
|
||||
curWidth += width
|
||||
@ -215,7 +224,7 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
|
||||
}
|
||||
|
||||
if curWidth > n {
|
||||
buf.WriteString(cluster)
|
||||
buf.Write(cluster)
|
||||
}
|
||||
|
||||
if ignoring {
|
||||
@ -250,7 +259,7 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
buf.WriteByte(s[i])
|
||||
buf.WriteByte(b[i])
|
||||
i++
|
||||
}
|
||||
|
||||
@ -269,22 +278,22 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
|
||||
// You can use this with [Truncate], [TruncateLeft], and [Cut].
|
||||
func ByteToGraphemeRange(str string, byteStart, byteStop int) (charStart, charStop int) {
|
||||
bytePos, charPos := 0, 0
|
||||
gr := graphemes.FromString(str)
|
||||
gr := uniseg.NewGraphemes(str)
|
||||
for byteStart > bytePos {
|
||||
if !gr.Next() {
|
||||
break
|
||||
}
|
||||
bytePos += len(gr.Value())
|
||||
charPos += max(1, displaywidth.String(gr.Value()))
|
||||
bytePos += len(gr.Str())
|
||||
charPos += max(1, gr.Width())
|
||||
}
|
||||
charStart = charPos
|
||||
for byteStop > bytePos {
|
||||
if !gr.Next() {
|
||||
break
|
||||
}
|
||||
bytePos += len(gr.Value())
|
||||
charPos += max(1, displaywidth.String(gr.Value()))
|
||||
bytePos += len(gr.Str())
|
||||
charPos += max(1, gr.Width())
|
||||
}
|
||||
charStop = charPos
|
||||
return charStart, charStop
|
||||
return
|
||||
}
|
||||
|
||||
17
vendor/github.com/charmbracelet/x/ansi/urxvt.go
generated
vendored
17
vendor/github.com/charmbracelet/x/ansi/urxvt.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
package ansi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// URxvtExt returns an escape sequence for calling a URxvt perl extension with
|
||||
// the given name and parameters.
|
||||
//
|
||||
// OSC 777 ; extension_name ; param1 ; param2 ; ... ST
|
||||
// OSC 777 ; extension_name ; param1 ; param2 ; ... BEL
|
||||
//
|
||||
// See: https://man.archlinux.org/man/extra/rxvt-unicode/urxvt.7.en#XTerm_Operating_System_Commands
|
||||
func URxvtExt(extension string, params ...string) string {
|
||||
return fmt.Sprintf("\x1b]777;%s;%s\x07", extension, strings.Join(params, ";"))
|
||||
}
|
||||
14
vendor/github.com/charmbracelet/x/ansi/width.go
generated
vendored
14
vendor/github.com/charmbracelet/x/ansi/width.go
generated
vendored
@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// Strip removes ANSI escape codes from a string.
|
||||
@ -81,16 +83,20 @@ func stringWidth(m Method, s string) int {
|
||||
}
|
||||
|
||||
var (
|
||||
pstate = parser.GroundState // initial state
|
||||
width int
|
||||
pstate = parser.GroundState // initial state
|
||||
cluster string
|
||||
width int
|
||||
)
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
state, action := parser.Table.Transition(pstate, s[i])
|
||||
if state == parser.Utf8State {
|
||||
cluster, w := FirstGraphemeCluster(s[i:], m)
|
||||
var w int
|
||||
cluster, _, w, _ = uniseg.FirstGraphemeClusterInString(s[i:], -1)
|
||||
if m == WcWidth {
|
||||
w = runewidth.StringWidth(cluster)
|
||||
}
|
||||
width += w
|
||||
|
||||
i += len(cluster) - 1
|
||||
pstate = parser.GroundState
|
||||
continue
|
||||
|
||||
50
vendor/github.com/charmbracelet/x/ansi/wrap.go
generated
vendored
50
vendor/github.com/charmbracelet/x/ansi/wrap.go
generated
vendored
@ -2,11 +2,12 @@ package ansi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// nbsp is a non-breaking space.
|
||||
@ -54,9 +55,12 @@ func hardwrap(m Method, s string, limit int, preserveSpace bool) string {
|
||||
i := 0
|
||||
for i < len(b) {
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
if state == parser.Utf8State {
|
||||
if state == parser.Utf8State { //nolint:nestif
|
||||
var width int
|
||||
cluster, width = FirstGraphemeCluster(b[i:], m)
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
i += len(cluster)
|
||||
|
||||
if curWidth+width > limit {
|
||||
@ -188,7 +192,10 @@ func wordwrap(m Method, s string, limit int, breakpoints string) string {
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
if state == parser.Utf8State { //nolint:nestif
|
||||
var width int
|
||||
cluster, width = FirstGraphemeCluster(b[i:], m)
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
i += len(cluster)
|
||||
|
||||
r, _ := utf8.DecodeRune(cluster)
|
||||
@ -296,7 +303,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
}
|
||||
|
||||
var (
|
||||
cluster string
|
||||
cluster []byte
|
||||
buf bytes.Buffer
|
||||
word bytes.Buffer
|
||||
space bytes.Buffer
|
||||
@ -304,12 +311,10 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
curWidth int // written width of the line
|
||||
wordLen int // word buffer len without ANSI escape codes
|
||||
pstate = parser.GroundState // initial state
|
||||
b = []byte(s)
|
||||
)
|
||||
|
||||
addSpace := func() {
|
||||
if spaceWidth == 0 && space.Len() == 0 {
|
||||
return
|
||||
}
|
||||
curWidth += spaceWidth
|
||||
buf.Write(space.Bytes())
|
||||
space.Reset()
|
||||
@ -336,27 +341,30 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
}
|
||||
|
||||
i := 0
|
||||
for i < len(s) {
|
||||
state, action := parser.Table.Transition(pstate, s[i])
|
||||
for i < len(b) {
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
if state == parser.Utf8State { //nolint:nestif
|
||||
var width int
|
||||
cluster, width = FirstGraphemeCluster(s[i:], m)
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
i += len(cluster)
|
||||
|
||||
r, _ := utf8.DecodeRuneInString(cluster)
|
||||
r, _ := utf8.DecodeRune(cluster)
|
||||
switch {
|
||||
case r != utf8.RuneError && unicode.IsSpace(r) && r != nbsp: // nbsp is a non-breaking space
|
||||
addWord()
|
||||
space.WriteRune(r)
|
||||
spaceWidth += width
|
||||
case strings.ContainsAny(cluster, breakpoints):
|
||||
case bytes.ContainsAny(cluster, breakpoints):
|
||||
addSpace()
|
||||
if curWidth+wordLen+width > limit {
|
||||
word.WriteString(cluster)
|
||||
word.Write(cluster)
|
||||
wordLen += width
|
||||
} else {
|
||||
addWord()
|
||||
buf.WriteString(cluster)
|
||||
buf.Write(cluster)
|
||||
curWidth += width
|
||||
}
|
||||
default:
|
||||
@ -365,17 +373,12 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
addWord()
|
||||
}
|
||||
|
||||
word.WriteString(cluster)
|
||||
word.Write(cluster)
|
||||
wordLen += width
|
||||
|
||||
if curWidth+wordLen+spaceWidth > limit {
|
||||
addNewline()
|
||||
}
|
||||
|
||||
if wordLen == limit {
|
||||
// Hardwrap the word if it's too long
|
||||
addWord()
|
||||
}
|
||||
}
|
||||
|
||||
pstate = parser.GroundState
|
||||
@ -384,7 +387,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
|
||||
switch action {
|
||||
case parser.PrintAction, parser.ExecuteAction:
|
||||
switch r := rune(s[i]); {
|
||||
switch r := rune(b[i]); {
|
||||
case r == '\n':
|
||||
if wordLen == 0 {
|
||||
if curWidth+spaceWidth > limit {
|
||||
@ -421,7 +424,6 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
if curWidth == limit {
|
||||
addNewline()
|
||||
}
|
||||
|
||||
word.WriteRune(r)
|
||||
wordLen++
|
||||
|
||||
@ -436,7 +438,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
}
|
||||
|
||||
default:
|
||||
word.WriteByte(s[i])
|
||||
word.WriteByte(b[i])
|
||||
}
|
||||
|
||||
// We manage the UTF8 state separately manually above.
|
||||
|
||||
15
vendor/github.com/charmbracelet/x/cellbuf/buffer.go
generated
vendored
15
vendor/github.com/charmbracelet/x/cellbuf/buffer.go
generated
vendored
@ -1,4 +1,3 @@
|
||||
// Package cellbuf provides terminal cell buffer functionality.
|
||||
package cellbuf
|
||||
|
||||
import (
|
||||
@ -25,7 +24,7 @@ func NewCell(r rune, comb ...rune) (c *Cell) {
|
||||
}
|
||||
c.Comb = comb
|
||||
c.Width = runewidth.StringWidth(string(append([]rune{r}, comb...)))
|
||||
return c
|
||||
return
|
||||
}
|
||||
|
||||
// NewCellString returns a new cell with the given string content. This is a
|
||||
@ -47,7 +46,7 @@ func NewCellString(s string) (c *Cell) {
|
||||
c.Comb = append(c.Comb, r)
|
||||
}
|
||||
}
|
||||
return c
|
||||
return
|
||||
}
|
||||
|
||||
// NewGraphemeCell returns a new cell. This is a convenience function that
|
||||
@ -72,7 +71,7 @@ func newGraphemeCell(s string, w int) (c *Cell) {
|
||||
c.Comb = append(c.Comb, r)
|
||||
}
|
||||
}
|
||||
return c
|
||||
return
|
||||
}
|
||||
|
||||
// Line represents a line in the terminal.
|
||||
@ -105,7 +104,7 @@ func (l Line) String() (s string) {
|
||||
}
|
||||
}
|
||||
s = strings.TrimRight(s, " ")
|
||||
return s
|
||||
return
|
||||
}
|
||||
|
||||
// At returns the cell at the given x position.
|
||||
@ -151,7 +150,7 @@ func (l Line) set(x int, c *Cell, clone bool) bool {
|
||||
for j := 1; j < maxCellWidth && x-j >= 0; j++ {
|
||||
wide := l.At(x - j)
|
||||
if wide != nil && wide.Width > 1 && j < wide.Width {
|
||||
for k := range wide.Width {
|
||||
for k := 0; k < wide.Width; k++ {
|
||||
l[x-j+k] = wide.Clone().Blank()
|
||||
}
|
||||
break
|
||||
@ -207,7 +206,7 @@ func (b *Buffer) String() (s string) {
|
||||
s += "\r\n"
|
||||
}
|
||||
}
|
||||
return s
|
||||
return
|
||||
}
|
||||
|
||||
// Line returns a pointer to the line at the given y position.
|
||||
@ -297,7 +296,7 @@ func (b *Buffer) FillRect(c *Cell, rect Rectangle) {
|
||||
}
|
||||
for y := rect.Min.Y; y < rect.Max.Y; y++ {
|
||||
for x := rect.Min.X; x < rect.Max.X; x += cellWidth {
|
||||
b.setCell(x, y, c, false)
|
||||
b.setCell(x, y, c, false) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
83
vendor/github.com/charmbracelet/x/cellbuf/cell.go
generated
vendored
83
vendor/github.com/charmbracelet/x/cellbuf/cell.go
generated
vendored
@ -96,7 +96,7 @@ func (c *Cell) Clear() bool {
|
||||
func (c *Cell) Clone() (n *Cell) {
|
||||
n = new(Cell)
|
||||
*n = *c
|
||||
return n
|
||||
return
|
||||
}
|
||||
|
||||
// Blank makes the cell a blank cell by setting the rune to a space, comb to
|
||||
@ -164,12 +164,12 @@ type UnderlineStyle = ansi.UnderlineStyle
|
||||
|
||||
// These are the available underline styles.
|
||||
const (
|
||||
NoUnderline = ansi.UnderlineStyleNone
|
||||
SingleUnderline = ansi.UnderlineStyleSingle
|
||||
DoubleUnderline = ansi.UnderlineStyleDouble
|
||||
CurlyUnderline = ansi.UnderlineStyleCurly
|
||||
DottedUnderline = ansi.UnderlineStyleDotted
|
||||
DashedUnderline = ansi.UnderlineStyleDashed
|
||||
NoUnderline = ansi.NoUnderlineStyle
|
||||
SingleUnderline = ansi.SingleUnderlineStyle
|
||||
DoubleUnderline = ansi.DoubleUnderlineStyle
|
||||
CurlyUnderline = ansi.CurlyUnderlineStyle
|
||||
DottedUnderline = ansi.DottedUnderlineStyle
|
||||
DashedUnderline = ansi.DashedUnderlineStyle
|
||||
)
|
||||
|
||||
// Style represents the Style of a cell.
|
||||
@ -189,7 +189,7 @@ func (s Style) Sequence() string {
|
||||
|
||||
var b ansi.Style
|
||||
|
||||
if s.Attrs != 0 { //nolint:nestif
|
||||
if s.Attrs != 0 {
|
||||
if s.Attrs&BoldAttr != 0 {
|
||||
b = b.Bold()
|
||||
}
|
||||
@ -197,31 +197,36 @@ func (s Style) Sequence() string {
|
||||
b = b.Faint()
|
||||
}
|
||||
if s.Attrs&ItalicAttr != 0 {
|
||||
b = b.Italic(true)
|
||||
b = b.Italic()
|
||||
}
|
||||
if s.Attrs&SlowBlinkAttr != 0 {
|
||||
b = b.Blink(true)
|
||||
b = b.SlowBlink()
|
||||
}
|
||||
if s.Attrs&RapidBlinkAttr != 0 {
|
||||
b = b.RapidBlink(true)
|
||||
b = b.RapidBlink()
|
||||
}
|
||||
if s.Attrs&ReverseAttr != 0 {
|
||||
b = b.Reverse(true)
|
||||
b = b.Reverse()
|
||||
}
|
||||
if s.Attrs&ConcealAttr != 0 {
|
||||
b = b.Conceal(true)
|
||||
b = b.Conceal()
|
||||
}
|
||||
if s.Attrs&StrikethroughAttr != 0 {
|
||||
b = b.Strikethrough(true)
|
||||
b = b.Strikethrough()
|
||||
}
|
||||
}
|
||||
if s.UlStyle != NoUnderline {
|
||||
switch u := s.UlStyle; u {
|
||||
case NoUnderline:
|
||||
b = b.Underline(false)
|
||||
default:
|
||||
b = b.Underline(true)
|
||||
b = b.UnderlineStyle(u)
|
||||
switch s.UlStyle {
|
||||
case SingleUnderline:
|
||||
b = b.Underline()
|
||||
case DoubleUnderline:
|
||||
b = b.DoubleUnderline()
|
||||
case CurlyUnderline:
|
||||
b = b.CurlyUnderline()
|
||||
case DottedUnderline:
|
||||
b = b.DottedUnderline()
|
||||
case DashedUnderline:
|
||||
b = b.DashedUnderline()
|
||||
}
|
||||
}
|
||||
if s.Fg != nil {
|
||||
@ -263,48 +268,64 @@ func (s Style) DiffSequence(o Style) string {
|
||||
isNormal bool
|
||||
)
|
||||
|
||||
if s.Attrs != o.Attrs { //nolint:nestif
|
||||
if s.Attrs != o.Attrs {
|
||||
if s.Attrs&BoldAttr != o.Attrs&BoldAttr {
|
||||
if s.Attrs&BoldAttr != 0 {
|
||||
b = b.Bold()
|
||||
} else if !isNormal {
|
||||
isNormal = true
|
||||
b = b.Normal()
|
||||
b = b.NormalIntensity()
|
||||
}
|
||||
}
|
||||
if s.Attrs&FaintAttr != o.Attrs&FaintAttr {
|
||||
if s.Attrs&FaintAttr != 0 {
|
||||
b = b.Faint()
|
||||
} else if !isNormal {
|
||||
b = b.Normal()
|
||||
b = b.NormalIntensity()
|
||||
}
|
||||
}
|
||||
if s.Attrs&ItalicAttr != o.Attrs&ItalicAttr {
|
||||
b = b.Italic(s.Attrs&ItalicAttr != 0)
|
||||
if s.Attrs&ItalicAttr != 0 {
|
||||
b = b.Italic()
|
||||
} else {
|
||||
b = b.NoItalic()
|
||||
}
|
||||
}
|
||||
if s.Attrs&SlowBlinkAttr != o.Attrs&SlowBlinkAttr {
|
||||
if s.Attrs&SlowBlinkAttr != 0 {
|
||||
b = b.Blink(true)
|
||||
b = b.SlowBlink()
|
||||
} else if !noBlink {
|
||||
noBlink = true
|
||||
b = b.Blink(false)
|
||||
b = b.NoBlink()
|
||||
}
|
||||
}
|
||||
if s.Attrs&RapidBlinkAttr != o.Attrs&RapidBlinkAttr {
|
||||
if s.Attrs&RapidBlinkAttr != 0 {
|
||||
b = b.RapidBlink(true)
|
||||
b = b.RapidBlink()
|
||||
} else if !noBlink {
|
||||
b = b.Blink(false)
|
||||
b = b.NoBlink()
|
||||
}
|
||||
}
|
||||
if s.Attrs&ReverseAttr != o.Attrs&ReverseAttr {
|
||||
b = b.Reverse(s.Attrs&ReverseAttr != 0)
|
||||
if s.Attrs&ReverseAttr != 0 {
|
||||
b = b.Reverse()
|
||||
} else {
|
||||
b = b.NoReverse()
|
||||
}
|
||||
}
|
||||
if s.Attrs&ConcealAttr != o.Attrs&ConcealAttr {
|
||||
b = b.Conceal(s.Attrs&ConcealAttr != 0)
|
||||
if s.Attrs&ConcealAttr != 0 {
|
||||
b = b.Conceal()
|
||||
} else {
|
||||
b = b.NoConceal()
|
||||
}
|
||||
}
|
||||
if s.Attrs&StrikethroughAttr != o.Attrs&StrikethroughAttr {
|
||||
b = b.Strikethrough(s.Attrs&StrikethroughAttr != 0)
|
||||
if s.Attrs&StrikethroughAttr != 0 {
|
||||
b = b.Strikethrough()
|
||||
} else {
|
||||
b = b.NoStrikethrough()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/charmbracelet/x/cellbuf/geom.go
generated
vendored
2
vendor/github.com/charmbracelet/x/cellbuf/geom.go
generated
vendored
@ -12,7 +12,7 @@ func Pos(x, y int) Position {
|
||||
return image.Pt(x, y)
|
||||
}
|
||||
|
||||
// Rectangle represents a rectangle.
|
||||
// Rectange represents a rectangle.
|
||||
type Rectangle = image.Rectangle
|
||||
|
||||
// Rect is a shorthand for Rectangle.
|
||||
|
||||
19
vendor/github.com/charmbracelet/x/cellbuf/hardscroll.go
generated
vendored
19
vendor/github.com/charmbracelet/x/cellbuf/hardscroll.go
generated
vendored
@ -75,7 +75,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
|
||||
)
|
||||
|
||||
blank := s.clearBlank()
|
||||
if n > 0 { //nolint:nestif
|
||||
if n > 0 {
|
||||
// Scroll up (forward)
|
||||
v = s.scrollUp(n, top, bot, 0, maxY, blank)
|
||||
if !v {
|
||||
@ -99,7 +99,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
|
||||
s.move(0, bot-n+1)
|
||||
s.clearToBottom(nil)
|
||||
} else {
|
||||
for i := range n {
|
||||
for i := 0; i < n; i++ {
|
||||
s.move(0, bot-i)
|
||||
s.clearToEnd(nil, false)
|
||||
}
|
||||
@ -124,7 +124,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
|
||||
// Clear newly shifted-in lines.
|
||||
if v &&
|
||||
(nonDestScrollRegion || (memoryBelow && top == 0)) {
|
||||
for i := range -n {
|
||||
for i := 0; i < -n; i++ {
|
||||
s.move(0, top+i)
|
||||
s.clearToEnd(nil, false)
|
||||
}
|
||||
@ -133,7 +133,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
|
||||
}
|
||||
|
||||
if !v {
|
||||
return v
|
||||
return
|
||||
}
|
||||
|
||||
s.scrollBuffer(s.curbuf, n, top, bot, blank)
|
||||
@ -193,7 +193,7 @@ func (s *Screen) touchLine(width, height, y, n int, changed bool) {
|
||||
|
||||
// scrollUp scrolls the screen up by n lines.
|
||||
func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
if n == 1 && top == minY && bot == maxY { //nolint:nestif
|
||||
if n == 1 && top == minY && bot == maxY {
|
||||
s.move(0, bot)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteByte('\n')
|
||||
@ -202,14 +202,13 @@ func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.DeleteLine(1))
|
||||
} else if top == minY && bot == maxY {
|
||||
supportsSU := s.caps.Contains(capSU)
|
||||
if supportsSU {
|
||||
if s.xtermLike {
|
||||
s.move(0, bot)
|
||||
} else {
|
||||
s.move(0, top)
|
||||
}
|
||||
s.updatePen(blank)
|
||||
if supportsSU {
|
||||
if s.xtermLike {
|
||||
s.buf.WriteString(ansi.ScrollUp(n))
|
||||
} else {
|
||||
s.buf.WriteString(strings.Repeat("\n", n))
|
||||
@ -226,7 +225,7 @@ func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
|
||||
// scrollDown scrolls the screen down by n lines.
|
||||
func (s *Screen) scrollDown(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
if n == 1 && top == minY && bot == maxY { //nolint:nestif
|
||||
if n == 1 && top == minY && bot == maxY {
|
||||
s.move(0, top)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.ReverseIndex)
|
||||
@ -237,7 +236,7 @@ func (s *Screen) scrollDown(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
} else if top == minY && bot == maxY {
|
||||
s.move(0, top)
|
||||
s.updatePen(blank)
|
||||
if s.caps.Contains(capSD) {
|
||||
if s.xtermLike {
|
||||
s.buf.WriteString(ansi.ScrollDown(n))
|
||||
} else {
|
||||
s.buf.WriteString(strings.Repeat(ansi.ReverseIndex, n))
|
||||
|
||||
16
vendor/github.com/charmbracelet/x/cellbuf/hashmap.go
generated
vendored
16
vendor/github.com/charmbracelet/x/cellbuf/hashmap.go
generated
vendored
@ -15,7 +15,7 @@ func hash(l Line) (h uint64) {
|
||||
}
|
||||
h += (h << 5) + uint64(r)
|
||||
}
|
||||
return h
|
||||
return
|
||||
}
|
||||
|
||||
// hashmap represents a single [Line] hash.
|
||||
@ -33,7 +33,7 @@ func (s *Screen) updateHashmap() {
|
||||
height := s.newbuf.Height()
|
||||
if len(s.oldhash) >= height && len(s.newhash) >= height {
|
||||
// rehash changed lines
|
||||
for i := range height {
|
||||
for i := 0; i < height; i++ {
|
||||
_, ok := s.touch[i]
|
||||
if ok {
|
||||
s.oldhash[i] = hash(s.curbuf.Line(i))
|
||||
@ -48,14 +48,14 @@ func (s *Screen) updateHashmap() {
|
||||
if len(s.newhash) != height {
|
||||
s.newhash = make([]uint64, height)
|
||||
}
|
||||
for i := range height {
|
||||
for i := 0; i < height; i++ {
|
||||
s.oldhash[i] = hash(s.curbuf.Line(i))
|
||||
s.newhash[i] = hash(s.newbuf.Line(i))
|
||||
}
|
||||
}
|
||||
|
||||
s.hashtab = make([]hashmap, height*2)
|
||||
for i := range height {
|
||||
for i := 0; i < height; i++ {
|
||||
hashval := s.oldhash[i]
|
||||
|
||||
// Find matching hash or empty slot
|
||||
@ -71,7 +71,7 @@ func (s *Screen) updateHashmap() {
|
||||
s.hashtab[idx].oldcount++
|
||||
s.hashtab[idx].oldindex = i
|
||||
}
|
||||
for i := range height {
|
||||
for i := 0; i < height; i++ {
|
||||
hashval := s.newhash[i]
|
||||
|
||||
// Find matching hash or empty slot
|
||||
@ -130,7 +130,7 @@ func (s *Screen) updateHashmap() {
|
||||
s.growHunks()
|
||||
}
|
||||
|
||||
// scrollOldhash.
|
||||
// scrollOldhash
|
||||
func (s *Screen) scrollOldhash(n, top, bot int) {
|
||||
if len(s.oldhash) == 0 {
|
||||
return
|
||||
@ -287,7 +287,7 @@ func (s *Screen) updateCost(from, to Line) (cost int) {
|
||||
cost++
|
||||
}
|
||||
}
|
||||
return cost
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Screen) updateCostBlank(to Line) (cost int) {
|
||||
@ -297,5 +297,5 @@ func (s *Screen) updateCostBlank(to Line) (cost int) {
|
||||
cost++
|
||||
}
|
||||
}
|
||||
return cost
|
||||
return
|
||||
}
|
||||
|
||||
2
vendor/github.com/charmbracelet/x/cellbuf/link.go
generated
vendored
2
vendor/github.com/charmbracelet/x/cellbuf/link.go
generated
vendored
@ -4,7 +4,7 @@ import (
|
||||
"github.com/charmbracelet/colorprofile"
|
||||
)
|
||||
|
||||
// ConvertLink converts a hyperlink to respect the given color profile.
|
||||
// Convert converts a hyperlink to respect the given color profile.
|
||||
func ConvertLink(h Link, p colorprofile.Profile) Link {
|
||||
if p == colorprofile.NoTTY {
|
||||
return Link{}
|
||||
|
||||
92
vendor/github.com/charmbracelet/x/cellbuf/pen.go
generated
vendored
92
vendor/github.com/charmbracelet/x/cellbuf/pen.go
generated
vendored
@ -1,92 +0,0 @@
|
||||
package cellbuf
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// PenWriter is a writer that writes to a buffer and keeps track of the current
|
||||
// pen style and link state for the purpose of wrapping with newlines.
|
||||
type PenWriter struct {
|
||||
w io.Writer
|
||||
p *ansi.Parser
|
||||
style Style
|
||||
link Link
|
||||
}
|
||||
|
||||
// NewPenWriter returns a new PenWriter.
|
||||
func NewPenWriter(w io.Writer) *PenWriter {
|
||||
pw := &PenWriter{w: w}
|
||||
pw.p = ansi.GetParser()
|
||||
handleCsi := func(cmd ansi.Cmd, params ansi.Params) {
|
||||
if cmd == 'm' {
|
||||
ReadStyle(params, &pw.style)
|
||||
}
|
||||
}
|
||||
handleOsc := func(cmd int, data []byte) {
|
||||
if cmd == 8 {
|
||||
ReadLink(data, &pw.link)
|
||||
}
|
||||
}
|
||||
pw.p.SetHandler(ansi.Handler{
|
||||
HandleCsi: handleCsi,
|
||||
HandleOsc: handleOsc,
|
||||
})
|
||||
return pw
|
||||
}
|
||||
|
||||
// Style returns the current pen style.
|
||||
func (w *PenWriter) Style() Style {
|
||||
return w.style
|
||||
}
|
||||
|
||||
// Link returns the current pen link.
|
||||
func (w *PenWriter) Link() Link {
|
||||
return w.link
|
||||
}
|
||||
|
||||
// Write writes to the buffer.
|
||||
func (w *PenWriter) Write(p []byte) (int, error) {
|
||||
for i := range p {
|
||||
b := p[i]
|
||||
w.p.Advance(b)
|
||||
if b == '\n' {
|
||||
if !w.style.Empty() {
|
||||
_, _ = w.w.Write([]byte(ansi.ResetStyle))
|
||||
}
|
||||
if !w.link.Empty() {
|
||||
_, _ = w.w.Write([]byte(ansi.ResetHyperlink()))
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = w.w.Write([]byte{b})
|
||||
if b == '\n' {
|
||||
if !w.link.Empty() {
|
||||
_, _ = w.w.Write([]byte(ansi.SetHyperlink(w.link.URL, w.link.Params)))
|
||||
}
|
||||
if !w.style.Empty() {
|
||||
_, _ = w.w.Write([]byte(w.style.Sequence()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Close closes the writer, resets the style and link if necessary, and releases
|
||||
// its parser. Calling it is performance critical, but forgetting it does not
|
||||
// cause safety issues or leaks.
|
||||
func (w *PenWriter) Close() error {
|
||||
if !w.style.Empty() {
|
||||
_, _ = w.w.Write([]byte(ansi.ResetStyle))
|
||||
}
|
||||
if !w.link.Empty() {
|
||||
_, _ = w.w.Write([]byte(ansi.ResetHyperlink()))
|
||||
}
|
||||
if w.p != nil {
|
||||
ansi.PutParser(w.p)
|
||||
w.p = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
221
vendor/github.com/charmbracelet/x/cellbuf/screen.go
generated
vendored
221
vendor/github.com/charmbracelet/x/cellbuf/screen.go
generated
vendored
@ -39,9 +39,9 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
var seq strings.Builder
|
||||
|
||||
width, height := s.newbuf.Width(), s.newbuf.Height()
|
||||
if ty != fy { //nolint:nestif
|
||||
if ty != fy {
|
||||
var yseq string
|
||||
if s.caps.Contains(capVPA) && !s.opts.RelativeCursor {
|
||||
if s.xtermLike && !s.opts.RelativeCursor {
|
||||
yseq = ansi.VerticalPositionAbsolute(ty + 1)
|
||||
}
|
||||
|
||||
@ -54,13 +54,9 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
}
|
||||
shouldScroll := !s.opts.AltScreen && fy+n >= s.scrollHeight
|
||||
if lf := strings.Repeat("\n", n); shouldScroll || (fy+n < height && len(lf) < len(yseq)) {
|
||||
//nolint:godox
|
||||
// TODO: Ensure we're not unintentionally scrolling the screen down.
|
||||
yseq = lf
|
||||
s.scrollHeight = max(s.scrollHeight, fy+n)
|
||||
if s.opts.MapNL {
|
||||
fx = 0
|
||||
}
|
||||
}
|
||||
} else if ty < fy {
|
||||
n := fy - ty
|
||||
@ -68,7 +64,6 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
yseq = cuu
|
||||
}
|
||||
if n == 1 && fy-1 > 0 {
|
||||
//nolint:godox
|
||||
// TODO: Ensure we're not unintentionally scrolling the screen up.
|
||||
yseq = ansi.ReverseIndex
|
||||
}
|
||||
@ -77,9 +72,9 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
seq.WriteString(yseq)
|
||||
}
|
||||
|
||||
if tx != fx { //nolint:nestif
|
||||
if tx != fx {
|
||||
var xseq string
|
||||
if s.caps.Contains(capHPA) && !s.opts.RelativeCursor {
|
||||
if s.xtermLike && !s.opts.RelativeCursor {
|
||||
xseq = ansi.HorizontalPositionAbsolute(tx + 1)
|
||||
}
|
||||
|
||||
@ -98,8 +93,7 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
if tabs > 0 {
|
||||
cht := ansi.CursorHorizontalForwardTab(tabs)
|
||||
tab := strings.Repeat("\t", tabs)
|
||||
if false && s.caps.Contains(capCHT) && len(cht) < len(tab) {
|
||||
//nolint:godox
|
||||
if false && s.xtermLike && len(cht) < len(tab) {
|
||||
// TODO: The linux console and some terminals such as
|
||||
// Alacritty don't support [ansi.CHT]. Enable this when
|
||||
// we have a way to detect this, or after 5 years when
|
||||
@ -150,7 +144,7 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
}
|
||||
} else if tx < fx {
|
||||
n := fx - tx
|
||||
if useTabs && s.caps.Contains(capCBT) {
|
||||
if useTabs && s.xtermLike {
|
||||
// VT100 does not support backward tabs [ansi.CBT].
|
||||
|
||||
col := fx
|
||||
@ -196,7 +190,7 @@ func moveCursor(s *Screen, x, y int, overwrite bool) (seq string) {
|
||||
// Method #0: Use [ansi.CUP] if the distance is long.
|
||||
seq = ansi.CursorPosition(x+1, y+1)
|
||||
if fx == -1 || fy == -1 || notLocal(s.newbuf.Width(), fx, fy, x, y) {
|
||||
return seq
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +234,7 @@ func moveCursor(s *Screen, x, y int, overwrite bool) (seq string) {
|
||||
}
|
||||
}
|
||||
|
||||
return seq
|
||||
return
|
||||
}
|
||||
|
||||
// moveCursor moves the cursor to the specified position.
|
||||
@ -248,10 +242,10 @@ func (s *Screen) moveCursor(x, y int, overwrite bool) {
|
||||
if !s.opts.AltScreen && s.cur.X == -1 && s.cur.Y == -1 {
|
||||
// First cursor movement in inline mode, move the cursor to the first
|
||||
// column before moving to the target position.
|
||||
s.buf.WriteByte('\r')
|
||||
s.buf.WriteByte('\r') //nolint:errcheck
|
||||
s.cur.X, s.cur.Y = 0, 0
|
||||
}
|
||||
s.buf.WriteString(moveCursor(s, x, y, overwrite))
|
||||
s.buf.WriteString(moveCursor(s, x, y, overwrite)) //nolint:errcheck
|
||||
s.cur.X, s.cur.Y = x, y
|
||||
}
|
||||
|
||||
@ -280,11 +274,10 @@ func (s *Screen) move(x, y int) {
|
||||
// Reset wrap around (phantom cursor) state
|
||||
if s.atPhantom {
|
||||
s.cur.X = 0
|
||||
s.buf.WriteByte('\r')
|
||||
s.atPhantom = false // reset phantom cell state
|
||||
s.buf.WriteByte('\r') //nolint:errcheck
|
||||
s.atPhantom = false // reset phantom cell state
|
||||
}
|
||||
|
||||
//nolint:godox
|
||||
// TODO: Investigate if we need to handle this case and/or if we need the
|
||||
// following code.
|
||||
//
|
||||
@ -298,7 +291,7 @@ func (s *Screen) move(x, y int) {
|
||||
//
|
||||
// if l > 0 {
|
||||
// s.cur.X = 0
|
||||
// s.buf.WriteString("\r" + strings.Repeat("\n", l))
|
||||
// s.buf.WriteString("\r" + strings.Repeat("\n", l)) //nolint:errcheck
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -346,10 +339,6 @@ type ScreenOptions struct {
|
||||
HardTabs bool
|
||||
// Backspace is whether to use backspace characters to move the cursor.
|
||||
Backspace bool
|
||||
// MapNL whether we have ONLCR mapping enabled. When we set the terminal to
|
||||
// raw mode, the ONLCR mode gets disabled. ONLCR maps any newline/linefeed
|
||||
// (`\n`) character to carriage return + line feed (`\r\n`).
|
||||
MapNL bool
|
||||
}
|
||||
|
||||
// lineData represents the metadata for a line.
|
||||
@ -376,13 +365,13 @@ type Screen struct {
|
||||
opts ScreenOptions
|
||||
mu sync.Mutex
|
||||
method ansi.Method
|
||||
scrollHeight int // keeps track of how many lines we've scrolled down (inline mode)
|
||||
altScreenMode bool // whether alternate screen mode is enabled
|
||||
cursorHidden bool // whether text cursor mode is enabled
|
||||
clear bool // whether to force clear the screen
|
||||
caps capabilities // terminal control sequence capabilities
|
||||
queuedText bool // whether we have queued non-zero width text queued up
|
||||
atPhantom bool // whether the cursor is out of bounds and at a phantom cell
|
||||
scrollHeight int // keeps track of how many lines we've scrolled down (inline mode)
|
||||
altScreenMode bool // whether alternate screen mode is enabled
|
||||
cursorHidden bool // whether text cursor mode is enabled
|
||||
clear bool // whether to force clear the screen
|
||||
xtermLike bool // whether to use xterm-like optimizations, otherwise, it uses vt100 only
|
||||
queuedText bool // whether we have queued non-zero width text queued up
|
||||
atPhantom bool // whether the cursor is out of bounds and at a phantom cell
|
||||
}
|
||||
|
||||
// SetMethod sets the method used to calculate the width of cells.
|
||||
@ -502,77 +491,36 @@ func (s *Screen) FillRect(cell *Cell, r Rectangle) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// capabilities represents a mask of supported ANSI escape sequences.
|
||||
type capabilities uint
|
||||
|
||||
const (
|
||||
// Vertical Position Absolute [ansi.VPA].
|
||||
capVPA capabilities = 1 << iota
|
||||
// Horizontal Position Absolute [ansi.HPA].
|
||||
capHPA
|
||||
// Cursor Horizontal Tab [ansi.CHT].
|
||||
capCHT
|
||||
// Cursor Backward Tab [ansi.CBT].
|
||||
capCBT
|
||||
// Repeat Previous Character [ansi.REP].
|
||||
capREP
|
||||
// Erase Character [ansi.ECH].
|
||||
capECH
|
||||
// Insert Character [ansi.ICH].
|
||||
capICH
|
||||
// Scroll Down [ansi.SD].
|
||||
capSD
|
||||
// Scroll Up [ansi.SU].
|
||||
capSU
|
||||
|
||||
noCaps capabilities = 0
|
||||
allCaps = capVPA | capHPA | capCHT | capCBT | capREP | capECH | capICH |
|
||||
capSD | capSU
|
||||
)
|
||||
|
||||
// Contains returns whether the capabilities contains the given capability.
|
||||
func (v capabilities) Contains(c capabilities) bool {
|
||||
return v&c == c
|
||||
}
|
||||
|
||||
// xtermCaps returns whether the terminal is xterm-like. This means that the
|
||||
// isXtermLike returns whether the terminal is xterm-like. This means that the
|
||||
// terminal supports ECMA-48 and ANSI X3.64 escape sequences.
|
||||
// xtermCaps returns a list of control sequence capabilities for the given
|
||||
// terminal type. This only supports a subset of sequences that can
|
||||
// be different among terminals.
|
||||
// NOTE: A hybrid approach would be to support Terminfo databases for a full
|
||||
// set of capabilities.
|
||||
func xtermCaps(termtype string) (v capabilities) {
|
||||
// TODO: Should this be a lookup table into each $TERM terminfo database? Like
|
||||
// we could keep a map of ANSI escape sequence to terminfo capability name and
|
||||
// check if the database supports the escape sequence. Instead of keeping a
|
||||
// list of terminal names here.
|
||||
func isXtermLike(termtype string) (v bool) {
|
||||
parts := strings.Split(termtype, "-")
|
||||
if len(parts) == 0 {
|
||||
return v
|
||||
return
|
||||
}
|
||||
|
||||
switch parts[0] {
|
||||
case
|
||||
"alacritty",
|
||||
"contour",
|
||||
"foot",
|
||||
"ghostty",
|
||||
"kitty",
|
||||
"linux",
|
||||
"rio",
|
||||
"screen",
|
||||
"st",
|
||||
"tmux",
|
||||
"wezterm",
|
||||
"xterm":
|
||||
v = allCaps
|
||||
case "alacritty":
|
||||
v = allCaps
|
||||
v &^= capCHT // NOTE: alacritty added support for [ansi.CHT] in 2024-12-28 #62d5b13.
|
||||
case "screen":
|
||||
// See https://www.gnu.org/software/screen/manual/screen.html#Control-Sequences-1
|
||||
v = allCaps
|
||||
v &^= capREP
|
||||
case "linux":
|
||||
// See https://man7.org/linux/man-pages/man4/console_codes.4.html
|
||||
v = capVPA | capHPA | capECH | capICH
|
||||
v = true
|
||||
}
|
||||
|
||||
return v
|
||||
return
|
||||
}
|
||||
|
||||
// NewScreen creates a new Screen.
|
||||
@ -600,14 +548,14 @@ func NewScreen(w io.Writer, width, height int, opts *ScreenOptions) (s *Screen)
|
||||
}
|
||||
|
||||
s.buf = new(bytes.Buffer)
|
||||
s.caps = xtermCaps(s.opts.Term)
|
||||
s.xtermLike = isXtermLike(s.opts.Term)
|
||||
s.curbuf = NewBuffer(width, height)
|
||||
s.newbuf = NewBuffer(width, height)
|
||||
s.cur = Cursor{Position: Pos(-1, -1)} // start at -1 to force a move
|
||||
s.saved = s.cur
|
||||
s.reset()
|
||||
|
||||
return s
|
||||
return
|
||||
}
|
||||
|
||||
// Width returns the width of the screen.
|
||||
@ -647,7 +595,7 @@ func (s *Screen) putCell(cell *Cell) {
|
||||
|
||||
// wrapCursor wraps the cursor to the next line.
|
||||
//
|
||||
|
||||
//nolint:unused
|
||||
func (s *Screen) wrapCursor() {
|
||||
const autoRightMargin = true
|
||||
if autoRightMargin {
|
||||
@ -680,9 +628,9 @@ func (s *Screen) putAttrCell(cell *Cell) {
|
||||
}
|
||||
|
||||
s.updatePen(cell)
|
||||
s.buf.WriteRune(cell.Rune)
|
||||
s.buf.WriteRune(cell.Rune) //nolint:errcheck
|
||||
for _, c := range cell.Comb {
|
||||
s.buf.WriteRune(c)
|
||||
s.buf.WriteRune(c) //nolint:errcheck
|
||||
}
|
||||
|
||||
s.cur.X += cell.Width
|
||||
@ -701,12 +649,12 @@ func (s *Screen) putCellLR(cell *Cell) {
|
||||
// Optimize for the lower right corner cell.
|
||||
curX := s.cur.X
|
||||
if cell == nil || !cell.Empty() {
|
||||
s.buf.WriteString(ansi.ResetModeAutoWrap)
|
||||
s.buf.WriteString(ansi.ResetAutoWrapMode) //nolint:errcheck
|
||||
s.putAttrCell(cell)
|
||||
// Writing to lower-right corner cell should not wrap.
|
||||
s.atPhantom = false
|
||||
s.cur.X = curX
|
||||
s.buf.WriteString(ansi.SetModeAutoWrap)
|
||||
s.buf.WriteString(ansi.SetAutoWrapMode) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
|
||||
@ -727,11 +675,11 @@ func (s *Screen) updatePen(cell *Cell) {
|
||||
if cell.Style.Empty() && len(seq) > len(ansi.ResetStyle) {
|
||||
seq = ansi.ResetStyle
|
||||
}
|
||||
s.buf.WriteString(seq)
|
||||
s.buf.WriteString(seq) //nolint:errcheck
|
||||
s.cur.Style = cell.Style
|
||||
}
|
||||
if !cell.Link.Equal(&s.cur.Link) {
|
||||
s.buf.WriteString(ansi.SetHyperlink(cell.Link.URL, cell.Link.Params))
|
||||
s.buf.WriteString(ansi.SetHyperlink(cell.Link.URL, cell.Link.Params)) //nolint:errcheck
|
||||
s.cur.Link = cell.Link
|
||||
}
|
||||
}
|
||||
@ -764,9 +712,9 @@ func (s *Screen) emitRange(line Line, n int) (eoi bool) {
|
||||
ech := ansi.EraseCharacter(count)
|
||||
cup := ansi.CursorPosition(s.cur.X+count, s.cur.Y)
|
||||
rep := ansi.RepeatPreviousCharacter(count)
|
||||
if s.caps.Contains(capECH) && count > len(ech)+len(cup) && cell0 != nil && cell0.Clear() { //nolint:nestif
|
||||
if s.xtermLike && count > len(ech)+len(cup) && cell0 != nil && cell0.Clear() {
|
||||
s.updatePen(cell0)
|
||||
s.buf.WriteString(ech)
|
||||
s.buf.WriteString(ech) //nolint:errcheck
|
||||
|
||||
// If this is the last cell, we don't need to move the cursor.
|
||||
if count < n {
|
||||
@ -774,7 +722,7 @@ func (s *Screen) emitRange(line Line, n int) (eoi bool) {
|
||||
} else {
|
||||
return true // cursor in the middle
|
||||
}
|
||||
} else if s.caps.Contains(capREP) && count > len(rep) &&
|
||||
} else if s.xtermLike && count > len(rep) &&
|
||||
(cell0 == nil || (len(cell0.Comb) == 0 && cell0.Rune < 256)) {
|
||||
// We only support ASCII characters. Most terminals will handle
|
||||
// non-ASCII characters correctly, but some might not, ahem xterm.
|
||||
@ -792,13 +740,13 @@ func (s *Screen) emitRange(line Line, n int) (eoi bool) {
|
||||
s.putCell(cell0)
|
||||
repCount-- // cell0 is a single width cell ASCII character
|
||||
|
||||
s.buf.WriteString(ansi.RepeatPreviousCharacter(repCount))
|
||||
s.buf.WriteString(ansi.RepeatPreviousCharacter(repCount)) //nolint:errcheck
|
||||
s.cur.X += repCount
|
||||
if wrapPossible {
|
||||
s.putCell(cell0)
|
||||
}
|
||||
} else {
|
||||
for i := range count {
|
||||
for i := 0; i < count; i++ {
|
||||
s.putCell(line.At(i))
|
||||
}
|
||||
}
|
||||
@ -807,7 +755,7 @@ func (s *Screen) emitRange(line Line, n int) (eoi bool) {
|
||||
n -= count
|
||||
}
|
||||
|
||||
return eoi
|
||||
return
|
||||
}
|
||||
|
||||
// putRange puts a range of cells from the old line to the new line.
|
||||
@ -817,7 +765,7 @@ func (s *Screen) putRange(oldLine, newLine Line, y, start, end int) (eoi bool) {
|
||||
inline := min(len(ansi.CursorPosition(start+1, y+1)),
|
||||
min(len(ansi.HorizontalPositionAbsolute(start+1)),
|
||||
len(ansi.CursorForward(start+1))))
|
||||
if (end - start + 1) > inline { //nolint:nestif
|
||||
if (end - start + 1) > inline {
|
||||
var j, same int
|
||||
for j, same = start, 0; j <= end; j++ {
|
||||
oldCell, newCell := oldLine.At(j), newLine.At(j)
|
||||
@ -869,9 +817,9 @@ func (s *Screen) clearToEnd(blank *Cell, force bool) { //nolint:unparam
|
||||
s.updatePen(blank)
|
||||
count := s.newbuf.Width() - s.cur.X
|
||||
if s.el0Cost() <= count {
|
||||
s.buf.WriteString(ansi.EraseLineRight)
|
||||
s.buf.WriteString(ansi.EraseLineRight) //nolint:errcheck
|
||||
} else {
|
||||
for range count {
|
||||
for i := 0; i < count; i++ {
|
||||
s.putCell(blank)
|
||||
}
|
||||
}
|
||||
@ -891,13 +839,12 @@ func (s *Screen) clearBlank() *Cell {
|
||||
// insertCells inserts the count cells pointed by the given line at the current
|
||||
// cursor position.
|
||||
func (s *Screen) insertCells(line Line, count int) {
|
||||
supportsICH := s.caps.Contains(capICH)
|
||||
if supportsICH {
|
||||
if s.xtermLike {
|
||||
// Use [ansi.ICH] as an optimization.
|
||||
s.buf.WriteString(ansi.InsertCharacter(count))
|
||||
s.buf.WriteString(ansi.InsertCharacter(count)) //nolint:errcheck
|
||||
} else {
|
||||
// Otherwise, use [ansi.IRM] mode.
|
||||
s.buf.WriteString(ansi.SetModeInsertReplace)
|
||||
s.buf.WriteString(ansi.SetInsertReplaceMode) //nolint:errcheck
|
||||
}
|
||||
|
||||
for i := 0; count > 0; i++ {
|
||||
@ -905,8 +852,8 @@ func (s *Screen) insertCells(line Line, count int) {
|
||||
count--
|
||||
}
|
||||
|
||||
if !supportsICH {
|
||||
s.buf.WriteString(ansi.ResetModeInsertReplace)
|
||||
if !s.xtermLike {
|
||||
s.buf.WriteString(ansi.ResetInsertReplaceMode) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
|
||||
@ -915,7 +862,7 @@ func (s *Screen) insertCells(line Line, count int) {
|
||||
// [ansi.EL] 0 i.e. [ansi.EraseLineRight] to clear
|
||||
// trailing spaces.
|
||||
func (s *Screen) el0Cost() int {
|
||||
if s.caps != noCaps {
|
||||
if s.xtermLike {
|
||||
return 0
|
||||
}
|
||||
return len(ansi.EraseLineRight)
|
||||
@ -931,7 +878,7 @@ func (s *Screen) transformLine(y int) {
|
||||
|
||||
// Find the first changed cell in the line
|
||||
var lineChanged bool
|
||||
for i := range s.newbuf.Width() {
|
||||
for i := 0; i < s.newbuf.Width(); i++ {
|
||||
if !cellEqual(newLine.At(i), oldLine.At(i)) {
|
||||
lineChanged = true
|
||||
break
|
||||
@ -939,7 +886,7 @@ func (s *Screen) transformLine(y int) {
|
||||
}
|
||||
|
||||
const ceolStandoutGlitch = false
|
||||
if ceolStandoutGlitch && lineChanged { //nolint:nestif
|
||||
if ceolStandoutGlitch && lineChanged {
|
||||
s.move(0, y)
|
||||
s.clearToEnd(nil, false)
|
||||
s.putRange(oldLine, newLine, y, 0, s.newbuf.Width()-1)
|
||||
@ -950,12 +897,12 @@ func (s *Screen) transformLine(y int) {
|
||||
// [ansi.EraseLineLeft].
|
||||
if blank == nil || blank.Clear() {
|
||||
var oFirstCell, nFirstCell int
|
||||
for oFirstCell = range s.curbuf.Width() {
|
||||
for oFirstCell = 0; oFirstCell < s.curbuf.Width(); oFirstCell++ {
|
||||
if !cellEqual(oldLine.At(oFirstCell), blank) {
|
||||
break
|
||||
}
|
||||
}
|
||||
for nFirstCell = range s.newbuf.Width() {
|
||||
for nFirstCell = 0; nFirstCell < s.newbuf.Width(); nFirstCell++ {
|
||||
if !cellEqual(newLine.At(nFirstCell), blank) {
|
||||
break
|
||||
}
|
||||
@ -978,11 +925,11 @@ func (s *Screen) transformLine(y int) {
|
||||
if nFirstCell >= s.newbuf.Width() {
|
||||
s.move(0, y)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.EraseLineRight)
|
||||
s.buf.WriteString(ansi.EraseLineRight) //nolint:errcheck
|
||||
} else {
|
||||
s.move(nFirstCell-1, y)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.EraseLineLeft)
|
||||
s.buf.WriteString(ansi.EraseLineLeft) //nolint:errcheck
|
||||
}
|
||||
|
||||
for firstCell < nFirstCell {
|
||||
@ -1098,7 +1045,7 @@ func (s *Screen) transformLine(y int) {
|
||||
|
||||
s.move(n+1, y)
|
||||
ichCost := 3 + nLastCell - oLastCell
|
||||
if s.caps.Contains(capICH) && (nLastCell < nLastNonBlank || ichCost > (m-n)) {
|
||||
if s.xtermLike && (nLastCell < nLastNonBlank || ichCost > (m-n)) {
|
||||
s.putRange(oldLine, newLine, y, n+1, m)
|
||||
} else {
|
||||
s.insertCells(newLine[n+1:], nLastCell-oLastCell)
|
||||
@ -1132,7 +1079,7 @@ func (s *Screen) transformLine(y int) {
|
||||
func (s *Screen) deleteCells(count int) {
|
||||
// [ansi.DCH] will shift in cells from the right margin so we need to
|
||||
// ensure that they are the right style.
|
||||
s.buf.WriteString(ansi.DeleteCharacter(count))
|
||||
s.buf.WriteString(ansi.DeleteCharacter(count)) //nolint:errcheck
|
||||
}
|
||||
|
||||
// clearToBottom clears the screen from the current cursor position to the end
|
||||
@ -1144,7 +1091,7 @@ func (s *Screen) clearToBottom(blank *Cell) {
|
||||
}
|
||||
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.EraseScreenBelow)
|
||||
s.buf.WriteString(ansi.EraseScreenBelow) //nolint:errcheck
|
||||
// Clear the rest of the current line
|
||||
s.curbuf.ClearRect(Rect(col, row, s.curbuf.Width()-col, 1))
|
||||
// Clear everything below the current line
|
||||
@ -1157,7 +1104,7 @@ func (s *Screen) clearToBottom(blank *Cell) {
|
||||
// It returns the top line.
|
||||
func (s *Screen) clearBottom(total int) (top int) {
|
||||
if total <= 0 {
|
||||
return top
|
||||
return
|
||||
}
|
||||
|
||||
top = total
|
||||
@ -1165,7 +1112,7 @@ func (s *Screen) clearBottom(total int) (top int) {
|
||||
blank := s.clearBlank()
|
||||
canClearWithBlank := blank == nil || blank.Clear()
|
||||
|
||||
if canClearWithBlank { //nolint:nestif
|
||||
if canClearWithBlank {
|
||||
var row int
|
||||
for row = total - 1; row >= 0; row-- {
|
||||
oldLine := s.curbuf.Line(row)
|
||||
@ -1200,14 +1147,14 @@ func (s *Screen) clearBottom(total int) (top int) {
|
||||
}
|
||||
}
|
||||
|
||||
return top
|
||||
return
|
||||
}
|
||||
|
||||
// clearScreen clears the screen and put cursor at home.
|
||||
func (s *Screen) clearScreen(blank *Cell) {
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.CursorHomePosition)
|
||||
s.buf.WriteString(ansi.EraseEntireScreen)
|
||||
s.buf.WriteString(ansi.CursorHomePosition) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.EraseEntireScreen) //nolint:errcheck
|
||||
s.cur.X, s.cur.Y = 0, 0
|
||||
s.curbuf.Fill(blank)
|
||||
}
|
||||
@ -1232,7 +1179,7 @@ func (s *Screen) clearUpdate() {
|
||||
s.clearBelow(blank, 0)
|
||||
}
|
||||
nonEmpty = s.clearBottom(nonEmpty)
|
||||
for i := range nonEmpty {
|
||||
for i := 0; i < nonEmpty; i++ {
|
||||
s.transformLine(i)
|
||||
}
|
||||
}
|
||||
@ -1247,13 +1194,13 @@ func (s *Screen) Flush() (err error) {
|
||||
func (s *Screen) flush() (err error) {
|
||||
// Write the buffer
|
||||
if s.buf.Len() > 0 {
|
||||
_, err = s.w.Write(s.buf.Bytes())
|
||||
_, err = s.w.Write(s.buf.Bytes()) //nolint:errcheck
|
||||
if err == nil {
|
||||
s.buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
return err //nolint:wrapcheck
|
||||
return
|
||||
}
|
||||
|
||||
// Render renders changes of the screen to the internal buffer. Call
|
||||
@ -1274,7 +1221,6 @@ func (s *Screen) render() {
|
||||
return
|
||||
}
|
||||
|
||||
//nolint:godox
|
||||
// TODO: Investigate whether this is necessary. Theoretically, terminals
|
||||
// can add/remove tab stops and we should be able to handle that. We could
|
||||
// use [ansi.DECTABSR] to read the tab stops, but that's not implemented in
|
||||
@ -1289,9 +1235,9 @@ func (s *Screen) render() {
|
||||
// Do we need alt-screen mode?
|
||||
if s.opts.AltScreen != s.altScreenMode {
|
||||
if s.opts.AltScreen {
|
||||
s.buf.WriteString(ansi.SetModeAltScreenSaveCursor)
|
||||
s.buf.WriteString(ansi.SetAltScreenSaveCursorMode)
|
||||
} else {
|
||||
s.buf.WriteString(ansi.ResetModeAltScreenSaveCursor)
|
||||
s.buf.WriteString(ansi.ResetAltScreenSaveCursorMode)
|
||||
}
|
||||
s.altScreenMode = s.opts.AltScreen
|
||||
}
|
||||
@ -1306,9 +1252,7 @@ func (s *Screen) render() {
|
||||
|
||||
// Do we have queued strings to write above the screen?
|
||||
if len(s.queueAbove) > 0 {
|
||||
//nolint:godox
|
||||
// TODO: Use scrolling region if available.
|
||||
//nolint:godox
|
||||
// TODO: Use [Screen.Write] [io.Writer] interface.
|
||||
|
||||
// We need to scroll the screen up by the number of lines in the queue.
|
||||
@ -1346,13 +1290,12 @@ func (s *Screen) render() {
|
||||
s.clearBelow(nil, s.newbuf.Height()-1)
|
||||
}
|
||||
|
||||
if s.clear { //nolint:nestif
|
||||
if s.clear {
|
||||
s.clearUpdate()
|
||||
s.clear = false
|
||||
} else if len(s.touch) > 0 {
|
||||
if s.opts.AltScreen {
|
||||
// Optimize scrolling for the alternate screen buffer.
|
||||
//nolint:godox
|
||||
// TODO: Should we optimize for inline mode as well? If so, we need
|
||||
// to know the actual cursor position to use [ansi.DECSTBM].
|
||||
s.scrollOptimize()
|
||||
@ -1368,7 +1311,7 @@ func (s *Screen) render() {
|
||||
}
|
||||
|
||||
nonEmpty = s.clearBottom(nonEmpty)
|
||||
for i = range nonEmpty {
|
||||
for i = 0; i < nonEmpty; i++ {
|
||||
_, ok := s.touch[i]
|
||||
if ok {
|
||||
s.transformLine(i)
|
||||
@ -1416,7 +1359,7 @@ func (s *Screen) Close() (err error) {
|
||||
s.move(0, s.newbuf.Height()-1)
|
||||
|
||||
if s.altScreenMode {
|
||||
s.buf.WriteString(ansi.ResetModeAltScreenSaveCursor)
|
||||
s.buf.WriteString(ansi.ResetAltScreenSaveCursorMode)
|
||||
s.altScreenMode = false
|
||||
}
|
||||
|
||||
@ -1428,11 +1371,11 @@ func (s *Screen) Close() (err error) {
|
||||
// Write the buffer
|
||||
err = s.flush()
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
s.reset()
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
// reset resets the screen to its initial state.
|
||||
@ -1477,9 +1420,9 @@ func (s *Screen) Resize(width, height int) bool {
|
||||
}
|
||||
|
||||
if height > oldh {
|
||||
s.ClearRect(Rect(0, max(oldh, 0), width, height-oldh))
|
||||
s.ClearRect(Rect(0, max(oldh-1, 0), width, height-oldh))
|
||||
} else if height < oldh {
|
||||
s.ClearRect(Rect(0, max(height, 0), width, oldh-height))
|
||||
s.ClearRect(Rect(0, max(height-1, 0), width, oldh-height))
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
|
||||
4
vendor/github.com/charmbracelet/x/cellbuf/style.go
generated
vendored
4
vendor/github.com/charmbracelet/x/cellbuf/style.go
generated
vendored
@ -4,9 +4,9 @@ import (
|
||||
"github.com/charmbracelet/colorprofile"
|
||||
)
|
||||
|
||||
// ConvertStyle converts a style to respect the given color profile.
|
||||
// Convert converts a style to respect the given color profile.
|
||||
func ConvertStyle(s Style, p colorprofile.Profile) Style {
|
||||
switch p { //nolint:exhaustive
|
||||
switch p {
|
||||
case colorprofile.TrueColor:
|
||||
return s
|
||||
case colorprofile.Ascii:
|
||||
|
||||
14
vendor/github.com/charmbracelet/x/cellbuf/utils.go
generated
vendored
14
vendor/github.com/charmbracelet/x/cellbuf/utils.go
generated
vendored
@ -9,6 +9,20 @@ func Height(s string) int {
|
||||
return strings.Count(s, "\n") + 1
|
||||
}
|
||||
|
||||
func min(a, b int) int { //nolint:predeclared
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func max(a, b int) int { //nolint:predeclared
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func clamp(v, low, high int) int {
|
||||
if high < low {
|
||||
low, high = high, low
|
||||
|
||||
20
vendor/github.com/charmbracelet/x/cellbuf/wrap.go
generated
vendored
20
vendor/github.com/charmbracelet/x/cellbuf/wrap.go
generated
vendored
@ -2,7 +2,6 @@ package cellbuf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"slices"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
@ -21,16 +20,6 @@ const nbsp = '\u00a0'
|
||||
//
|
||||
// Note: breakpoints must be a string of 1-cell wide rune characters.
|
||||
func Wrap(s string, limit int, breakpoints string) string {
|
||||
//nolint:godox
|
||||
// TODO: Use [PenWriter] once we get
|
||||
// https://github.com/charmbracelet/lipgloss/pull/489 out the door and
|
||||
// released.
|
||||
// The problem is that [ansi.Wrap] doesn't keep track of style and link
|
||||
// state, so combining both breaks styled space cells. To fix this, we use
|
||||
// non-breaking space cells for padding and styled blank cells. And since
|
||||
// both wrapping methods respect non-breaking spaces, we can use them to
|
||||
// preserve styled spaces in the output.
|
||||
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
@ -101,7 +90,7 @@ func Wrap(s string, limit int, breakpoints string) string {
|
||||
seq, width, n, newState := ansi.DecodeSequence(s, state, p)
|
||||
switch width {
|
||||
case 0:
|
||||
if ansi.Equal(seq, "\t") { //nolint:nestif
|
||||
if ansi.Equal(seq, "\t") {
|
||||
addWord()
|
||||
space.WriteString(seq)
|
||||
break
|
||||
@ -187,5 +176,10 @@ func Wrap(s string, limit int, breakpoints string) string {
|
||||
}
|
||||
|
||||
func runeContainsAny[T string | []rune](r rune, s T) bool {
|
||||
return slices.Contains([]rune(s), r)
|
||||
for _, c := range []rune(s) {
|
||||
if c == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
29
vendor/github.com/charmbracelet/x/cellbuf/writer.go
generated
vendored
29
vendor/github.com/charmbracelet/x/cellbuf/writer.go
generated
vendored
@ -25,7 +25,7 @@ type CellBuffer interface {
|
||||
func FillRect(s CellBuffer, c *Cell, rect Rectangle) {
|
||||
for y := rect.Min.Y; y < rect.Max.Y; y++ {
|
||||
for x := rect.Min.X; x < rect.Max.X; x++ {
|
||||
s.SetCell(x, y, c)
|
||||
s.SetCell(x, y, c) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,7 +68,7 @@ func SetContent(s CellBuffer, str string) {
|
||||
func Render(d CellBuffer) string {
|
||||
var buf bytes.Buffer
|
||||
height := d.Bounds().Dy()
|
||||
for y := range height {
|
||||
for y := 0; y < height; y++ {
|
||||
_, line := RenderLine(d, y)
|
||||
buf.WriteString(line)
|
||||
if y < height-1 {
|
||||
@ -98,32 +98,32 @@ func RenderLine(d CellBuffer, n int) (w int, line string) {
|
||||
pendingLine = ""
|
||||
}
|
||||
|
||||
for x := range d.Bounds().Dx() {
|
||||
if cell := d.Cell(x, n); cell != nil && cell.Width > 0 { //nolint:nestif
|
||||
for x := 0; x < d.Bounds().Dx(); x++ {
|
||||
if cell := d.Cell(x, n); cell != nil && cell.Width > 0 {
|
||||
// Convert the cell's style and link to the given color profile.
|
||||
cellStyle := cell.Style
|
||||
cellLink := cell.Link
|
||||
if cellStyle.Empty() && !pen.Empty() {
|
||||
writePending()
|
||||
buf.WriteString(ansi.ResetStyle)
|
||||
buf.WriteString(ansi.ResetStyle) //nolint:errcheck
|
||||
pen.Reset()
|
||||
}
|
||||
if !cellStyle.Equal(&pen) {
|
||||
writePending()
|
||||
seq := cellStyle.DiffSequence(pen)
|
||||
buf.WriteString(seq)
|
||||
buf.WriteString(seq) // nolint:errcheck
|
||||
pen = cellStyle
|
||||
}
|
||||
|
||||
// Write the URL escape sequence
|
||||
if cellLink != link && link.URL != "" {
|
||||
writePending()
|
||||
buf.WriteString(ansi.ResetHyperlink())
|
||||
buf.WriteString(ansi.ResetHyperlink()) //nolint:errcheck
|
||||
link.Reset()
|
||||
}
|
||||
if cellLink != link {
|
||||
writePending()
|
||||
buf.WriteString(ansi.SetHyperlink(cellLink.URL, cellLink.Params))
|
||||
buf.WriteString(ansi.SetHyperlink(cellLink.URL, cellLink.Params)) //nolint:errcheck
|
||||
link = cellLink
|
||||
}
|
||||
|
||||
@ -140,10 +140,10 @@ func RenderLine(d CellBuffer, n int) (w int, line string) {
|
||||
}
|
||||
}
|
||||
if link.URL != "" {
|
||||
buf.WriteString(ansi.ResetHyperlink())
|
||||
buf.WriteString(ansi.ResetHyperlink()) //nolint:errcheck
|
||||
}
|
||||
if !pen.Empty() {
|
||||
buf.WriteString(ansi.ResetStyle)
|
||||
buf.WriteString(ansi.ResetStyle) //nolint:errcheck
|
||||
}
|
||||
return w, strings.TrimRight(buf.String(), " ") // Trim trailing spaces
|
||||
}
|
||||
@ -201,7 +201,7 @@ func (s *ScreenWriter) SetContentRect(str string, rect Rectangle) {
|
||||
// string to the width of the screen if it exceeds the width of the screen.
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
|
||||
// sequences.
|
||||
func (s *ScreenWriter) Print(str string, v ...any) {
|
||||
func (s *ScreenWriter) Print(str string, v ...interface{}) {
|
||||
if len(v) > 0 {
|
||||
str = fmt.Sprintf(str, v...)
|
||||
}
|
||||
@ -214,7 +214,7 @@ func (s *ScreenWriter) Print(str string, v ...any) {
|
||||
// the width of the screen if it exceeds the width of the screen.
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
|
||||
// sequences.
|
||||
func (s *ScreenWriter) PrintAt(x, y int, str string, v ...any) {
|
||||
func (s *ScreenWriter) PrintAt(x, y int, str string, v ...interface{}) {
|
||||
if len(v) > 0 {
|
||||
str = fmt.Sprintf(str, v...)
|
||||
}
|
||||
@ -299,7 +299,7 @@ func printString[T []byte | string](
|
||||
// Print the cell to the screen
|
||||
cell.Style = style
|
||||
cell.Link = link
|
||||
s.SetCell(x, y, &cell)
|
||||
s.SetCell(x, y, &cell) //nolint:errcheck
|
||||
x += width
|
||||
}
|
||||
}
|
||||
@ -309,7 +309,6 @@ func printString[T []byte | string](
|
||||
cell.Reset()
|
||||
default:
|
||||
// Valid sequences always have a non-zero Cmd.
|
||||
//nolint:godox
|
||||
// TODO: Handle cursor movement and other sequences
|
||||
switch {
|
||||
case ansi.HasCsiPrefix(seq) && p.Command() == 'm':
|
||||
@ -334,7 +333,7 @@ func printString[T []byte | string](
|
||||
|
||||
// Make sure to set the last cell if it's not empty.
|
||||
if !cell.Empty() {
|
||||
s.SetCell(x, y, &cell)
|
||||
s.SetCell(x, y, &cell) //nolint:errcheck
|
||||
cell.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
2
vendor/github.com/charmbracelet/x/term/term.go
generated
vendored
2
vendor/github.com/charmbracelet/x/term/term.go
generated
vendored
@ -1,5 +1,3 @@
|
||||
// Package term provides a platform-independent interfaces for interacting with
|
||||
// Terminal and TTY devices.
|
||||
package term
|
||||
|
||||
// State contains platform-specific state of a terminal.
|
||||
|
||||
118
vendor/github.com/charmbracelet/x/term/term_plan9.go
generated
vendored
118
vendor/github.com/charmbracelet/x/term/term_plan9.go
generated
vendored
@ -1,118 +0,0 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type state struct {
|
||||
termName string
|
||||
raw bool
|
||||
ctl *os.File
|
||||
}
|
||||
|
||||
// termName returns the name of the terminal or os.ErrNotExist if there is no terminal.
|
||||
func termName(fd uintptr) (string, error) {
|
||||
ctl, err := os.ReadFile(filepath.Join("/fd", fmt.Sprintf("%dctl", fd)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
f := strings.Fields(string(ctl))
|
||||
if len(f) == 0 {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
return f[len(f)-1], nil
|
||||
}
|
||||
|
||||
func isTerminal(fd uintptr) bool {
|
||||
ctl, err := os.ReadFile(filepath.Join("/fd", fmt.Sprintf("%dctl", fd)))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(string(ctl), "/dev/cons") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func makeRaw(fd uintptr) (*State, error) {
|
||||
t, err := termName(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctl, err := os.OpenFile(t, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := ctl.Write([]byte("rawon")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{state: state{termName: t, raw: true, ctl: ctl}}, nil
|
||||
}
|
||||
|
||||
func getState(fd uintptr) (*State, error) {
|
||||
t, err := termName(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctl, err := os.OpenFile(t, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{state: state{termName: t, raw: false, ctl: ctl}}, nil
|
||||
|
||||
}
|
||||
|
||||
func restore(_ uintptr, state *State) error {
|
||||
if _, err := state.ctl.Write([]byte("rawoff")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSize returns the size. This will only work if you are running
|
||||
// under a window manager in Plan 9. Else, the only option
|
||||
// is to return a reasonable default.
|
||||
func getSize(fd uintptr) (int, int, error) {
|
||||
w, h := 80, 40
|
||||
b, err := os.ReadFile("/dev/wctl")
|
||||
if err != nil {
|
||||
return w, h, err
|
||||
}
|
||||
f := strings.Fields(string(b))
|
||||
if len(f) != 4 {
|
||||
return w, h, fmt.Errorf("%q only has %d of 4 needed fields:%w", f, len(f), os.ErrInvalid)
|
||||
}
|
||||
// The contents of wctl, as defined in the driver, are
|
||||
// 4 12-char fields: upper left x, y; and lower-right x, y
|
||||
var ulx, uly, lrx, lry int
|
||||
if n, err := fmt.Sscanf(string(b[:48]), "%d%d%d%d", &ulx, &uly, &lrx, &lry); n != 4 || err != nil {
|
||||
return w, h, fmt.Errorf("scanning %q:%d of 4 items scanned:%w", string(b[:48]), n, err)
|
||||
}
|
||||
|
||||
w, h = lrx-lrx, lry-uly
|
||||
return w, h, nil
|
||||
}
|
||||
|
||||
func setState(_ uintptr, state *State) error {
|
||||
raw := "rawoff"
|
||||
if state.raw {
|
||||
raw = "rawon"
|
||||
}
|
||||
if _, err := state.ctl.Write([]byte(raw)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readPassword(fd uintptr) ([]byte, error) {
|
||||
f := os.NewFile(fd, "cons")
|
||||
var b [128]byte
|
||||
n, err := f.Read(b[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
20
vendor/github.com/charmbracelet/x/term/term_unix.go
generated
vendored
20
vendor/github.com/charmbracelet/x/term/term_unix.go
generated
vendored
@ -19,7 +19,7 @@ func isTerminal(fd uintptr) bool {
|
||||
func makeRaw(fd uintptr) (*State, error) {
|
||||
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err //nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldState := State{state{Termios: *termios}}
|
||||
@ -34,7 +34,7 @@ func makeRaw(fd uintptr) (*State, error) {
|
||||
termios.Cc[unix.VMIN] = 1
|
||||
termios.Cc[unix.VTIME] = 0
|
||||
if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios); err != nil {
|
||||
return nil, err //nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oldState, nil
|
||||
@ -45,26 +45,26 @@ func setState(fd uintptr, state *State) error {
|
||||
if state != nil {
|
||||
termios = &state.Termios
|
||||
}
|
||||
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios) //nolint:wrapcheck
|
||||
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios)
|
||||
}
|
||||
|
||||
func getState(fd uintptr) (*State, error) {
|
||||
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err //nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &State{state{Termios: *termios}}, nil
|
||||
}
|
||||
|
||||
func restore(fd uintptr, state *State) error {
|
||||
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.Termios) //nolint:wrapcheck
|
||||
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.Termios)
|
||||
}
|
||||
|
||||
func getSize(fd uintptr) (width, height int, err error) {
|
||||
ws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
|
||||
if err != nil {
|
||||
return 0, 0, err //nolint:wrapcheck
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(ws.Col), int(ws.Row), nil
|
||||
}
|
||||
@ -73,13 +73,13 @@ func getSize(fd uintptr) (width, height int, err error) {
|
||||
type passwordReader int
|
||||
|
||||
func (r passwordReader) Read(buf []byte) (int, error) {
|
||||
return unix.Read(int(r), buf) //nolint:wrapcheck
|
||||
return unix.Read(int(r), buf)
|
||||
}
|
||||
|
||||
func readPassword(fd uintptr) ([]byte, error) {
|
||||
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err //nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newState := *termios
|
||||
@ -87,10 +87,10 @@ func readPassword(fd uintptr) ([]byte, error) {
|
||||
newState.Lflag |= unix.ICANON | unix.ISIG
|
||||
newState.Iflag |= unix.ICRNL
|
||||
if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &newState); err != nil {
|
||||
return nil, err //nolint:wrapcheck
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios) //nolint:errcheck
|
||||
defer unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios)
|
||||
|
||||
return readPasswordLine(passwordReader(fd))
|
||||
}
|
||||
|
||||
2
vendor/github.com/charmbracelet/x/term/term_windows.go
generated
vendored
2
vendor/github.com/charmbracelet/x/term/term_windows.go
generated
vendored
@ -24,7 +24,7 @@ func makeRaw(fd uintptr) (*State, error) {
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT)
|
||||
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||
raw |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||
if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
|
||||
return nil, err
|
||||
|
||||
2
vendor/github.com/charmbracelet/x/term/util.go
generated
vendored
2
vendor/github.com/charmbracelet/x/term/util.go
generated
vendored
@ -41,7 +41,7 @@ func readPasswordLine(reader io.Reader) ([]byte, error) {
|
||||
if err == io.EOF && len(ret) > 0 {
|
||||
return ret, nil
|
||||
}
|
||||
return ret, err //nolint:wrapcheck
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
vendor/github.com/clipperhouse/displaywidth/.gitignore
generated
vendored
2
vendor/github.com/clipperhouse/displaywidth/.gitignore
generated
vendored
@ -1,2 +0,0 @@
|
||||
.DS_Store
|
||||
*.out
|
||||
37
vendor/github.com/clipperhouse/displaywidth/AGENTS.md
generated
vendored
37
vendor/github.com/clipperhouse/displaywidth/AGENTS.md
generated
vendored
@ -1,37 +0,0 @@
|
||||
The goals and overview of this package can be found in the README.md file,
|
||||
start by reading that.
|
||||
|
||||
The goal of this package is to determine the display (column) width of a
|
||||
string, UTF-8 bytes, or runes, as would happen in a monospace font, especially
|
||||
in a terminal.
|
||||
|
||||
When troubleshooting, write Go unit tests instead of executing debug scripts.
|
||||
The tests can return whatever logs or output you need. If those tests are
|
||||
only for temporary troubleshooting, clean up the tests after the debugging is
|
||||
done.
|
||||
|
||||
(Separate executable debugging scripts are messy, tend to have conflicting
|
||||
dependencies and are hard to cleanup.)
|
||||
|
||||
If you make changes to the trie generation in internal/gen, it can be invoked
|
||||
by running `go generate` from the top package directory.
|
||||
|
||||
## Pull Requests and branches
|
||||
|
||||
For PRs (pull requests), you can use the gh CLI tool to retrieve details,
|
||||
or post comments. Then, compare the current branch with main. Reviewing a PR
|
||||
and reviewing a branch are about the same, but the PR may add context.
|
||||
|
||||
Look for bugs. Think like GitHub Copilot or Cursor BugBot.
|
||||
|
||||
Offer to post a brief summary of the review to the PR, via the gh CLI tool.
|
||||
|
||||
## Comparisons to go-runewidth
|
||||
|
||||
We originally attempted to make this package compatible with go-runewidth.
|
||||
However, we found that there were too many differences in the handling of
|
||||
certain characters and properties.
|
||||
|
||||
We believe, preliminarily, that our choices are more correct and complete,
|
||||
by using more complete categories such as Unicode Cf (format) for zero-width
|
||||
and Mn (Nonspacing_Mark) for combining marks.
|
||||
70
vendor/github.com/clipperhouse/displaywidth/CHANGELOG.md
generated
vendored
70
vendor/github.com/clipperhouse/displaywidth/CHANGELOG.md
generated
vendored
@ -1,70 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## [0.6.1]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.0...v0.6.1)
|
||||
|
||||
### Changed
|
||||
- Perf improvements: replaced the ASCII lookup table with a simple
|
||||
function. A bit more cache-friendly. More inlining.
|
||||
- Bug fix: single regional indicators are now treated as width 2, since that
|
||||
is what actual terminals do.
|
||||
|
||||
## [0.6.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.5.0...v0.6.0)
|
||||
|
||||
### Added
|
||||
- New `StringGraphemes` and `BytesGraphemes` methods, for iterating over the
|
||||
widths of grapheme clusters.
|
||||
|
||||
### Changed
|
||||
- Added ASCII fast paths
|
||||
|
||||
## [0.5.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.4.1...v0.5.0)
|
||||
|
||||
### Added
|
||||
- Unicode 16 support
|
||||
- Improved emoji presentation handling per Unicode TR51
|
||||
|
||||
### Changed
|
||||
- Corrected VS15 (U+FE0E) handling: now preserves base character width (no-op) per Unicode TR51
|
||||
- Performance optimizations: reduced property lookups
|
||||
|
||||
### Fixed
|
||||
- VS15 variation selector now correctly preserves base character width instead of forcing width 1
|
||||
|
||||
## [0.4.1]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.4.0...v0.4.1)
|
||||
|
||||
### Changed
|
||||
- Updated uax29 dependency
|
||||
- Improved flag handling
|
||||
|
||||
## [0.4.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.3.1...v0.4.0)
|
||||
|
||||
### Added
|
||||
- Support for variation selectors (VS15, VS16) and regional indicator pairs (flags)
|
||||
|
||||
## [0.3.1]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.3.0...v0.3.1)
|
||||
|
||||
### Added
|
||||
- Fuzz testing support
|
||||
|
||||
### Changed
|
||||
- Updated stringish dependency
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.2.0...v0.3.0)
|
||||
|
||||
### Changed
|
||||
- Dropped compatibility with go-runewidth
|
||||
- Trie implementation cleanup
|
||||
21
vendor/github.com/clipperhouse/displaywidth/LICENSE
generated
vendored
21
vendor/github.com/clipperhouse/displaywidth/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Matt Sherman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
171
vendor/github.com/clipperhouse/displaywidth/README.md
generated
vendored
171
vendor/github.com/clipperhouse/displaywidth/README.md
generated
vendored
@ -1,171 +0,0 @@
|
||||
# displaywidth
|
||||
|
||||
A high-performance Go package for measuring the monospace display width of strings, UTF-8 bytes, and runes.
|
||||
|
||||
[](https://pkg.go.dev/github.com/clipperhouse/displaywidth)
|
||||
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gotest.yml)
|
||||
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gofuzz.yml)
|
||||
|
||||
## Install
|
||||
```bash
|
||||
go get github.com/clipperhouse/displaywidth
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/clipperhouse/displaywidth"
|
||||
)
|
||||
|
||||
func main() {
|
||||
width := displaywidth.String("Hello, 世界!")
|
||||
fmt.Println(width)
|
||||
|
||||
width = displaywidth.Bytes([]byte("🌍"))
|
||||
fmt.Println(width)
|
||||
|
||||
width = displaywidth.Rune('🌍')
|
||||
fmt.Println(width)
|
||||
}
|
||||
```
|
||||
|
||||
For most purposes, you should use the `String` or `Bytes` methods. They sum
|
||||
the widths of grapheme clusters in the string or byte slice.
|
||||
|
||||
> Note: in your application, iterating over runes to measure width is likely incorrect;
|
||||
the smallest unit of display is a grapheme, not a rune.
|
||||
|
||||
### Iterating over graphemes
|
||||
|
||||
If you need the individual graphemes:
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/clipperhouse/displaywidth"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g := displaywidth.StringGraphemes("Hello, 世界!")
|
||||
for g.Next() {
|
||||
width := g.Width()
|
||||
value := g.Value()
|
||||
// do something with the width or value
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
There is one option, `displaywidth.Options.EastAsianWidth`, which defines
|
||||
how [East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous)
|
||||
are treated.
|
||||
|
||||
When `false` (default), East Asian Ambiguous characters are treated as width 1.
|
||||
When `true`, they are treated as width 2.
|
||||
|
||||
You may wish to configure this based on environment variables or locale.
|
||||
`go-runewidth`, for example, does so
|
||||
[during package initialization](https://github.com/mattn/go-runewidth/blob/master/runewidth.go#L26C1-L45C2).
|
||||
|
||||
`displaywidth` does not do this automatically, we prefer to leave it to you.
|
||||
You might do something like:
|
||||
|
||||
```go
|
||||
var width displaywidth.Options // zero value is default
|
||||
|
||||
func init() {
|
||||
if os.Getenv("EAST_ASIAN_WIDTH") == "true" {
|
||||
width = displaywidth.Options{EastAsianWidth: true}
|
||||
}
|
||||
// or check locale, or any other logic you want
|
||||
}
|
||||
|
||||
// use it in your logic
|
||||
func myApp() {
|
||||
fmt.Println(width.String("Hello, 世界!"))
|
||||
}
|
||||
```
|
||||
|
||||
## Technical standards and compatibility
|
||||
|
||||
This package implements the Unicode East Asian Width standard
|
||||
([UAX #11](https://www.unicode.org/reports/tr11/tr11-43.html)), and handles
|
||||
[version selectors](https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)),
|
||||
and [regional indicator pairs](https://en.wikipedia.org/wiki/Regional_indicator_symbol)
|
||||
(flags). We implement [Unicode TR51](https://www.unicode.org/reports/tr51/tr51-27.html). We are keeping
|
||||
an eye on [emerging standards](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/).
|
||||
|
||||
|
||||
`clipperhouse/displaywidth`, `mattn/go-runewidth`, and `rivo/uniseg` will
|
||||
give the same outputs for most real-world text. Extensive details are in the
|
||||
[compatibility analysis](comparison/COMPATIBILITY_ANALYSIS.md).
|
||||
|
||||
If you wish to investigate the core logic, see the `lookupProperties` and `width`
|
||||
functions in [width.go](width.go#L139). The essential trie generation logic is in
|
||||
`buildPropertyBitmap` in [unicode.go](internal/gen/unicode.go#L316).
|
||||
|
||||
|
||||
## Prior Art
|
||||
|
||||
[mattn/go-runewidth](https://github.com/mattn/go-runewidth)
|
||||
|
||||
[rivo/uniseg](https://github.com/rivo/uniseg)
|
||||
|
||||
[x/text/width](https://pkg.go.dev/golang.org/x/text/width)
|
||||
|
||||
[x/text/internal/triegen](https://pkg.go.dev/golang.org/x/text/internal/triegen)
|
||||
|
||||
## Benchmarks
|
||||
|
||||
```bash
|
||||
cd comparison
|
||||
go test -bench=. -benchmem
|
||||
```
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/clipperhouse/displaywidth/comparison
|
||||
cpu: Apple M2
|
||||
|
||||
BenchmarkString_Mixed/clipperhouse/displaywidth-8 10400 ns/op 162.21 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Mixed/mattn/go-runewidth-8 14296 ns/op 118.00 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Mixed/rivo/uniseg-8 19770 ns/op 85.33 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkString_EastAsian/clipperhouse/displaywidth-8 10593 ns/op 159.26 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_EastAsian/mattn/go-runewidth-8 23980 ns/op 70.35 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_EastAsian/rivo/uniseg-8 19777 ns/op 85.30 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkString_ASCII/clipperhouse/displaywidth-8 1032 ns/op 124.09 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_ASCII/mattn/go-runewidth-8 1162 ns/op 110.16 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_ASCII/rivo/uniseg-8 1586 ns/op 80.69 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkString_Emoji/clipperhouse/displaywidth-8 3017 ns/op 240.01 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Emoji/mattn/go-runewidth-8 4745 ns/op 152.58 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Emoji/rivo/uniseg-8 6745 ns/op 107.34 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3381 ns/op 498.90 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_Mixed/mattn/go-runewidth-8 5383 ns/op 313.41 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3395 ns/op 496.96 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_EastAsian/mattn/go-runewidth-8 15645 ns/op 107.83 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_ASCII/clipperhouse/displaywidth-8 257.8 ns/op 496.57 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_ASCII/mattn/go-runewidth-8 267.3 ns/op 478.89 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1338 ns/op 541.24 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_Emoji/mattn/go-runewidth-8 2287 ns/op 316.58 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkTruncateWithTail/clipperhouse/displaywidth-8 3689 ns/op 47.98 MB/s 192 B/op 14 allocs/op
|
||||
BenchmarkTruncateWithTail/mattn/go-runewidth-8 8069 ns/op 21.93 MB/s 192 B/op 14 allocs/op
|
||||
|
||||
BenchmarkTruncateWithoutTail/clipperhouse/displaywidth-8 3457 ns/op 66.24 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkTruncateWithoutTail/mattn/go-runewidth-8 10441 ns/op 21.93 MB/s 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
Here are some notes on [how to make Unicode things fast](https://clipperhouse.com/go-unicode/).
|
||||
3
vendor/github.com/clipperhouse/displaywidth/gen.go
generated
vendored
3
vendor/github.com/clipperhouse/displaywidth/gen.go
generated
vendored
@ -1,3 +0,0 @@
|
||||
package displaywidth
|
||||
|
||||
//go:generate go run -C internal/gen .
|
||||
72
vendor/github.com/clipperhouse/displaywidth/graphemes.go
generated
vendored
72
vendor/github.com/clipperhouse/displaywidth/graphemes.go
generated
vendored
@ -1,72 +0,0 @@
|
||||
package displaywidth
|
||||
|
||||
import (
|
||||
"github.com/clipperhouse/stringish"
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
)
|
||||
|
||||
// Graphemes is an iterator over grapheme clusters.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
type Graphemes[T stringish.Interface] struct {
|
||||
iter graphemes.Iterator[T]
|
||||
options Options
|
||||
}
|
||||
|
||||
// Next advances the iterator to the next grapheme cluster.
|
||||
func (g *Graphemes[T]) Next() bool {
|
||||
return g.iter.Next()
|
||||
}
|
||||
|
||||
// Value returns the current grapheme cluster.
|
||||
func (g *Graphemes[T]) Value() T {
|
||||
return g.iter.Value()
|
||||
}
|
||||
|
||||
// Width returns the display width of the current grapheme cluster.
|
||||
func (g *Graphemes[T]) Width() int {
|
||||
return graphemeWidth(g.Value(), g.options)
|
||||
}
|
||||
|
||||
// StringGraphemes returns an iterator over grapheme clusters for the given
|
||||
// string.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func StringGraphemes(s string) Graphemes[string] {
|
||||
return DefaultOptions.StringGraphemes(s)
|
||||
}
|
||||
|
||||
// StringGraphemes returns an iterator over grapheme clusters for the given
|
||||
// string, with the given options.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func (options Options) StringGraphemes(s string) Graphemes[string] {
|
||||
return Graphemes[string]{
|
||||
iter: graphemes.FromString(s),
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// BytesGraphemes returns an iterator over grapheme clusters for the given
|
||||
// []byte.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func BytesGraphemes(s []byte) Graphemes[[]byte] {
|
||||
return DefaultOptions.BytesGraphemes(s)
|
||||
}
|
||||
|
||||
// BytesGraphemes returns an iterator over grapheme clusters for the given
|
||||
// []byte, with the given options.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func (options Options) BytesGraphemes(s []byte) Graphemes[[]byte] {
|
||||
return Graphemes[[]byte]{
|
||||
iter: graphemes.FromBytes(s),
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
1696
vendor/github.com/clipperhouse/displaywidth/trie.go
generated
vendored
1696
vendor/github.com/clipperhouse/displaywidth/trie.go
generated
vendored
File diff suppressed because it is too large
Load Diff
236
vendor/github.com/clipperhouse/displaywidth/width.go
generated
vendored
236
vendor/github.com/clipperhouse/displaywidth/width.go
generated
vendored
@ -1,236 +0,0 @@
|
||||
package displaywidth
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/clipperhouse/stringish"
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
)
|
||||
|
||||
// Options allows you to specify the treatment of ambiguous East Asian
|
||||
// characters. When EastAsianWidth is false (default), ambiguous East Asian
|
||||
// characters are treated as width 1. When EastAsianWidth is true, ambiguous
|
||||
// East Asian characters are treated as width 2.
|
||||
type Options struct {
|
||||
EastAsianWidth bool
|
||||
}
|
||||
|
||||
// DefaultOptions is the default options for the display width
|
||||
// calculation, which is EastAsianWidth: false.
|
||||
var DefaultOptions = Options{EastAsianWidth: false}
|
||||
|
||||
// String calculates the display width of a string,
|
||||
// by iterating over grapheme clusters in the string
|
||||
// and summing their widths.
|
||||
func String(s string) int {
|
||||
return DefaultOptions.String(s)
|
||||
}
|
||||
|
||||
// String calculates the display width of a string, for the given options, by
|
||||
// iterating over grapheme clusters in the string and summing their widths.
|
||||
func (options Options) String(s string) int {
|
||||
// Optimization: no need to parse grapheme
|
||||
switch len(s) {
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return asciiWidth(s[0])
|
||||
}
|
||||
|
||||
width := 0
|
||||
g := graphemes.FromString(s)
|
||||
for g.Next() {
|
||||
width += graphemeWidth(g.Value(), options)
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
// Bytes calculates the display width of a []byte,
|
||||
// by iterating over grapheme clusters in the byte slice
|
||||
// and summing their widths.
|
||||
func Bytes(s []byte) int {
|
||||
return DefaultOptions.Bytes(s)
|
||||
}
|
||||
|
||||
// Bytes calculates the display width of a []byte, for the given options, by
|
||||
// iterating over grapheme clusters in the slice and summing their widths.
|
||||
func (options Options) Bytes(s []byte) int {
|
||||
// Optimization: no need to parse grapheme
|
||||
switch len(s) {
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return asciiWidth(s[0])
|
||||
}
|
||||
|
||||
width := 0
|
||||
g := graphemes.FromBytes(s)
|
||||
for g.Next() {
|
||||
width += graphemeWidth(g.Value(), options)
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
// Rune calculates the display width of a rune. You
|
||||
// should almost certainly use [String] or [Bytes] for
|
||||
// most purposes.
|
||||
//
|
||||
// The smallest unit of display width is a grapheme
|
||||
// cluster, not a rune. Iterating over runes to measure
|
||||
// width is incorrect in many cases.
|
||||
func Rune(r rune) int {
|
||||
return DefaultOptions.Rune(r)
|
||||
}
|
||||
|
||||
// Rune calculates the display width of a rune, for the given options.
|
||||
//
|
||||
// You should almost certainly use [String] or [Bytes] for most purposes.
|
||||
//
|
||||
// The smallest unit of display width is a grapheme cluster, not a rune.
|
||||
// Iterating over runes to measure width is incorrect in many cases.
|
||||
func (options Options) Rune(r rune) int {
|
||||
if r < utf8.RuneSelf {
|
||||
return asciiWidth(byte(r))
|
||||
}
|
||||
|
||||
// Surrogates (U+D800-U+DFFF) are invalid UTF-8.
|
||||
if r >= 0xD800 && r <= 0xDFFF {
|
||||
return 0
|
||||
}
|
||||
|
||||
var buf [4]byte
|
||||
n := utf8.EncodeRune(buf[:], r)
|
||||
|
||||
// Skip the grapheme iterator
|
||||
return graphemeWidth(buf[:n], options)
|
||||
}
|
||||
|
||||
const _Default property = 0
|
||||
|
||||
// TruncateString truncates a string to the given maxWidth, and appends the
|
||||
// given tail if the string is truncated.
|
||||
//
|
||||
// It ensures the total width, including the width of the tail, is less than or
|
||||
// equal to maxWidth.
|
||||
func (options Options) TruncateString(s string, maxWidth int, tail string) string {
|
||||
maxWidthWithoutTail := maxWidth - options.String(tail)
|
||||
|
||||
var pos, total int
|
||||
g := graphemes.FromString(s)
|
||||
for g.Next() {
|
||||
gw := graphemeWidth(g.Value(), options)
|
||||
if total+gw <= maxWidthWithoutTail {
|
||||
pos = g.End()
|
||||
}
|
||||
total += gw
|
||||
if total > maxWidth {
|
||||
return s[:pos] + tail
|
||||
}
|
||||
}
|
||||
// No truncation
|
||||
return s
|
||||
}
|
||||
|
||||
// TruncateString truncates a string to the given maxWidth, and appends the
|
||||
// given tail if the string is truncated.
|
||||
//
|
||||
// It ensures the total width, including the width of the tail, is less than or
|
||||
// equal to maxWidth.
|
||||
func TruncateString(s string, maxWidth int, tail string) string {
|
||||
return DefaultOptions.TruncateString(s, maxWidth, tail)
|
||||
}
|
||||
|
||||
// TruncateBytes truncates a []byte to the given maxWidth, and appends the
|
||||
// given tail if the []byte is truncated.
|
||||
//
|
||||
// It ensures the total width, including the width of the tail, is less than or
|
||||
// equal to maxWidth.
|
||||
func (options Options) TruncateBytes(s []byte, maxWidth int, tail []byte) []byte {
|
||||
maxWidthWithoutTail := maxWidth - options.Bytes(tail)
|
||||
|
||||
var pos, total int
|
||||
g := graphemes.FromBytes(s)
|
||||
for g.Next() {
|
||||
gw := graphemeWidth(g.Value(), options)
|
||||
if total+gw <= maxWidthWithoutTail {
|
||||
pos = g.End()
|
||||
}
|
||||
total += gw
|
||||
if total > maxWidth {
|
||||
result := make([]byte, 0, pos+len(tail))
|
||||
result = append(result, s[:pos]...)
|
||||
result = append(result, tail...)
|
||||
return result
|
||||
}
|
||||
}
|
||||
// No truncation
|
||||
return s
|
||||
}
|
||||
|
||||
// TruncateBytes truncates a []byte to the given maxWidth, and appends the
|
||||
// given tail if the []byte is truncated.
|
||||
//
|
||||
// It ensures the total width, including the width of the tail, is less than or
|
||||
// equal to maxWidth.
|
||||
func TruncateBytes(s []byte, maxWidth int, tail []byte) []byte {
|
||||
return DefaultOptions.TruncateBytes(s, maxWidth, tail)
|
||||
}
|
||||
|
||||
// graphemeWidth returns the display width of a grapheme cluster.
|
||||
// The passed string must be a single grapheme cluster.
|
||||
func graphemeWidth[T stringish.Interface](s T, options Options) int {
|
||||
// Optimization: no need to look up properties
|
||||
switch len(s) {
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return asciiWidth(s[0])
|
||||
}
|
||||
|
||||
p, sz := lookup(s)
|
||||
prop := property(p)
|
||||
|
||||
// Variation Selector 16 (VS16) requests emoji presentation
|
||||
if prop != _Wide && sz > 0 && len(s) >= sz+3 {
|
||||
vs := s[sz : sz+3]
|
||||
if isVS16(vs) {
|
||||
prop = _Wide
|
||||
}
|
||||
// VS15 (0x8E) requests text presentation but does not affect width,
|
||||
// in my reading of Unicode TR51. Falls through to return the base
|
||||
// character's property.
|
||||
}
|
||||
|
||||
if options.EastAsianWidth && prop == _East_Asian_Ambiguous {
|
||||
prop = _Wide
|
||||
}
|
||||
|
||||
if prop > upperBound {
|
||||
prop = _Default
|
||||
}
|
||||
|
||||
return propertyWidths[prop]
|
||||
}
|
||||
|
||||
func asciiWidth(b byte) int {
|
||||
if b <= 0x1F || b == 0x7F {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// isVS16 checks if the slice matches VS16 (U+FE0F) UTF-8 encoding
|
||||
// (EF B8 8F). It assumes len(s) >= 3.
|
||||
func isVS16[T stringish.Interface](s T) bool {
|
||||
return s[0] == 0xEF && s[1] == 0xB8 && s[2] == 0x8F
|
||||
}
|
||||
|
||||
// propertyWidths is a jump table of sorts, instead of a switch
|
||||
var propertyWidths = [4]int{
|
||||
_Default: 1,
|
||||
_Zero_Width: 0,
|
||||
_Wide: 2,
|
||||
_East_Asian_Ambiguous: 1,
|
||||
}
|
||||
|
||||
const upperBound = property(len(propertyWidths) - 1)
|
||||
2
vendor/github.com/clipperhouse/stringish/.gitignore
generated
vendored
2
vendor/github.com/clipperhouse/stringish/.gitignore
generated
vendored
@ -1,2 +0,0 @@
|
||||
.DS_Store
|
||||
*.test
|
||||
21
vendor/github.com/clipperhouse/stringish/LICENSE
generated
vendored
21
vendor/github.com/clipperhouse/stringish/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Matt Sherman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
64
vendor/github.com/clipperhouse/stringish/README.md
generated
vendored
64
vendor/github.com/clipperhouse/stringish/README.md
generated
vendored
@ -1,64 +0,0 @@
|
||||
# stringish
|
||||
|
||||
A small Go module that provides a generic type constraint for “string-like”
|
||||
data, and a utf8 package that works with both strings and byte slices
|
||||
without conversions.
|
||||
|
||||
```go
|
||||
type Interface interface {
|
||||
~[]byte | ~string
|
||||
}
|
||||
```
|
||||
|
||||
[](https://pkg.go.dev/github.com/clipperhouse/stringish/utf8)
|
||||
[](https://github.com/clipperhouse/stringish/actions/workflows/gotest.yml)
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
go get github.com/clipperhouse/stringish
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/clipperhouse/stringish"
|
||||
"github.com/clipperhouse/stringish/utf8"
|
||||
)
|
||||
|
||||
s := "Hello, 世界"
|
||||
r, size := utf8.DecodeRune(s) // not DecodeRuneInString 🎉
|
||||
|
||||
b := []byte("Hello, 世界")
|
||||
r, size = utf8.DecodeRune(b) // same API!
|
||||
|
||||
func MyFoo[T stringish.Interface](s T) T {
|
||||
// pass a string or a []byte
|
||||
// iterate, slice, transform, whatever
|
||||
}
|
||||
```
|
||||
|
||||
## Motivation
|
||||
|
||||
Sometimes we want APIs to accept `string` or `[]byte` without having to convert
|
||||
between those types. That conversion usually allocates!
|
||||
|
||||
By implementing with `stringish.Interface`, we can have a single API, and
|
||||
single implementation for both types: one `Foo` instead of `Foo` and
|
||||
`FooString`.
|
||||
|
||||
We have converted the
|
||||
[`unicode/utf8` package](https://github.com/clipperhouse/stringish/blob/main/utf8/utf8.go)
|
||||
as an example -- note the absence of`*InString` funcs. We might look at `x/text`
|
||||
next.
|
||||
|
||||
## Used by
|
||||
|
||||
- clipperhouse/uax29: [stringish trie](https://github.com/clipperhouse/uax29/blob/master/graphemes/trie.go#L27), [stringish iterator](https://github.com/clipperhouse/uax29/blob/master/internal/iterators/iterator.go#L9), [stringish SplitFunc](https://github.com/clipperhouse/uax29/blob/master/graphemes/splitfunc.go#L21)
|
||||
|
||||
- [clipperhouse/displaywidth](https://github.com/clipperhouse/displaywidth)
|
||||
|
||||
## Prior discussion
|
||||
|
||||
- [Consideration of similar by the Go team](https://github.com/golang/go/issues/48643)
|
||||
5
vendor/github.com/clipperhouse/stringish/interface.go
generated
vendored
5
vendor/github.com/clipperhouse/stringish/interface.go
generated
vendored
@ -1,5 +0,0 @@
|
||||
package stringish
|
||||
|
||||
type Interface interface {
|
||||
~[]byte | ~string
|
||||
}
|
||||
24
vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md
generated
vendored
24
vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md
generated
vendored
@ -1,9 +1,5 @@
|
||||
An implementation of grapheme cluster boundaries from [Unicode text segmentation](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (UAX 29), for Unicode version 15.0.0.
|
||||
|
||||
[](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes)
|
||||

|
||||

|
||||
|
||||
## Quick start
|
||||
|
||||
```
|
||||
@ -22,14 +18,15 @@ for tokens.Next() { // Next() returns true until end of data
|
||||
}
|
||||
```
|
||||
|
||||
[](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes)
|
||||
|
||||
_A grapheme is a “single visible character”, which might be a simple as a single letter, or a complex emoji that consists of several Unicode code points._
|
||||
|
||||
## Conformance
|
||||
|
||||
We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Tests29).
|
||||
We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Tests29). Status:
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## APIs
|
||||
|
||||
@ -74,18 +71,9 @@ for tokens.Next() { // Next() returns true until end of data
|
||||
}
|
||||
```
|
||||
|
||||
### Benchmarks
|
||||
### Performance
|
||||
|
||||
On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second, and no allocations.
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/clipperhouse/uax29/graphemes/comparative
|
||||
cpu: Apple M2
|
||||
BenchmarkGraphemes/clipperhouse/uax29-8 173805 ns/op 201.16 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkGraphemes/rivo/uniseg-8 2045128 ns/op 17.10 MB/s 0 B/op 0 allocs/op
|
||||
```
|
||||
On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second. You should see ~constant memory, and no allocations.
|
||||
|
||||
### Invalid inputs
|
||||
|
||||
|
||||
7
vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go
generated
vendored
7
vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go
generated
vendored
@ -1,11 +1,8 @@
|
||||
package graphemes
|
||||
|
||||
import (
|
||||
"github.com/clipperhouse/stringish"
|
||||
"github.com/clipperhouse/uax29/v2/internal/iterators"
|
||||
)
|
||||
import "github.com/clipperhouse/uax29/v2/internal/iterators"
|
||||
|
||||
type Iterator[T stringish.Interface] struct {
|
||||
type Iterator[T iterators.Stringish] struct {
|
||||
*iterators.Iterator[T]
|
||||
}
|
||||
|
||||
|
||||
4
vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go
generated
vendored
4
vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go
generated
vendored
@ -3,7 +3,7 @@ package graphemes
|
||||
import (
|
||||
"bufio"
|
||||
|
||||
"github.com/clipperhouse/stringish"
|
||||
"github.com/clipperhouse/uax29/v2/internal/iterators"
|
||||
)
|
||||
|
||||
// is determines if lookup intersects propert(ies)
|
||||
@ -18,7 +18,7 @@ const _Ignore = _Extend
|
||||
// See https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries.
|
||||
var SplitFunc bufio.SplitFunc = splitFunc[[]byte]
|
||||
|
||||
func splitFunc[T stringish.Interface](data T, atEOF bool) (advance int, token T, err error) {
|
||||
func splitFunc[T iterators.Stringish](data T, atEOF bool) (advance int, token T, err error) {
|
||||
var empty T
|
||||
if len(data) == 0 {
|
||||
return 0, empty, nil
|
||||
|
||||
6
vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go
generated
vendored
6
vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go
generated
vendored
@ -1,10 +1,10 @@
|
||||
package graphemes
|
||||
|
||||
import "github.com/clipperhouse/stringish"
|
||||
|
||||
// generated by github.com/clipperhouse/uax29/v2
|
||||
// from https://www.unicode.org/Public/15.0.0/ucd/auxiliary/GraphemeBreakProperty.txt
|
||||
|
||||
import "github.com/clipperhouse/uax29/v2/internal/iterators"
|
||||
|
||||
type property uint16
|
||||
|
||||
const (
|
||||
@ -27,7 +27,7 @@ const (
|
||||
// lookup returns the trie value for the first UTF-8 encoding in s and
|
||||
// the width in bytes of this encoding. The size will be 0 if s does not
|
||||
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
|
||||
func lookup[T stringish.Interface](s T) (v property, sz int) {
|
||||
func lookup[T iterators.Stringish](s T) (v property, sz int) {
|
||||
c0 := s[0]
|
||||
switch {
|
||||
case c0 < 0x80: // is ASCII
|
||||
|
||||
27
vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go
generated
vendored
27
vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go
generated
vendored
@ -1,12 +1,14 @@
|
||||
package iterators
|
||||
|
||||
import "github.com/clipperhouse/stringish"
|
||||
type Stringish interface {
|
||||
[]byte | string
|
||||
}
|
||||
|
||||
type SplitFunc[T stringish.Interface] func(T, bool) (int, T, error)
|
||||
type SplitFunc[T Stringish] func(T, bool) (int, T, error)
|
||||
|
||||
// Iterator is a generic iterator for words that are either []byte or string.
|
||||
// Iterate while Next() is true, and access the word via Value().
|
||||
type Iterator[T stringish.Interface] struct {
|
||||
type Iterator[T Stringish] struct {
|
||||
split SplitFunc[T]
|
||||
data T
|
||||
start int
|
||||
@ -14,7 +16,7 @@ type Iterator[T stringish.Interface] struct {
|
||||
}
|
||||
|
||||
// New creates a new Iterator for the given data and SplitFunc.
|
||||
func New[T stringish.Interface](split SplitFunc[T], data T) *Iterator[T] {
|
||||
func New[T Stringish](split SplitFunc[T], data T) *Iterator[T] {
|
||||
return &Iterator[T]{
|
||||
split: split,
|
||||
data: data,
|
||||
@ -81,20 +83,3 @@ func (iter *Iterator[T]) Reset() {
|
||||
iter.start = 0
|
||||
iter.pos = 0
|
||||
}
|
||||
|
||||
func (iter *Iterator[T]) First() T {
|
||||
if len(iter.data) == 0 {
|
||||
return iter.data
|
||||
}
|
||||
advance, _, err := iter.split(iter.data, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if advance <= 0 {
|
||||
panic("SplitFunc returned a zero or negative advance")
|
||||
}
|
||||
if advance > len(iter.data) {
|
||||
panic("SplitFunc advanced beyond the end of the data")
|
||||
}
|
||||
return iter.data[:advance]
|
||||
}
|
||||
|
||||
6
vendor/github.com/cloudflare/circl/internal/sha3/xor_unaligned.go
generated
vendored
6
vendor/github.com/cloudflare/circl/internal/sha3/xor_unaligned.go
generated
vendored
@ -14,14 +14,14 @@ import "unsafe"
|
||||
type storageBuf [maxRate / 8]uint64
|
||||
|
||||
func (b *storageBuf) asBytes() *[maxRate]byte {
|
||||
return (*[maxRate]byte)(unsafe.Pointer(b)) //nolint:gosec
|
||||
return (*[maxRate]byte)(unsafe.Pointer(b))
|
||||
}
|
||||
|
||||
// xorInuses unaligned reads and writes to update d.a to contain d.a
|
||||
// XOR buf.
|
||||
func xorIn(d *State, buf []byte) {
|
||||
n := len(buf)
|
||||
bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0]))[: n/8 : n/8] //nolint:gosec
|
||||
bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0]))[: n/8 : n/8]
|
||||
if n >= 72 {
|
||||
d.a[0] ^= bw[0]
|
||||
d.a[1] ^= bw[1]
|
||||
@ -56,6 +56,6 @@ func xorIn(d *State, buf []byte) {
|
||||
}
|
||||
|
||||
func copyOut(d *State, buf []byte) {
|
||||
ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0])) //nolint:gosec
|
||||
ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0]))
|
||||
copy(buf, ab[:])
|
||||
}
|
||||
|
||||
6
vendor/github.com/cloudflare/circl/sign/sign.go
generated
vendored
6
vendor/github.com/cloudflare/circl/sign/sign.go
generated
vendored
@ -38,12 +38,6 @@ type PrivateKey interface {
|
||||
encoding.BinaryMarshaler
|
||||
}
|
||||
|
||||
// A private key that retains the seed with which it was generated.
|
||||
type Seeded interface {
|
||||
// returns the seed if retained, otherwise nil
|
||||
Seed() []byte
|
||||
}
|
||||
|
||||
// A Scheme represents a specific instance of a signature scheme.
|
||||
type Scheme interface {
|
||||
// Name of the scheme.
|
||||
|
||||
4
vendor/github.com/cyphar/filepath-securejoin/.golangci.yml
generated
vendored
4
vendor/github.com/cyphar/filepath-securejoin/.golangci.yml
generated
vendored
@ -9,10 +9,6 @@
|
||||
|
||||
version: "2"
|
||||
|
||||
run:
|
||||
build-tags:
|
||||
- libpathrs
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- asasalint
|
||||
|
||||
82
vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
generated
vendored
82
vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
generated
vendored
@ -6,82 +6,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased] ##
|
||||
|
||||
## [0.6.1] - 2025-11-19 ##
|
||||
|
||||
> At last up jumped the cunning spider, and fiercely held her fast.
|
||||
|
||||
### Fixed ###
|
||||
- Our logic for deciding whether to use `openat2(2)` or fallback to an `O_PATH`
|
||||
resolver would cache the result to avoid doing needless test runs of
|
||||
`openat2(2)`. However, this causes issues when `pathrs-lite` is being used by
|
||||
a program that applies new seccomp-bpf filters onto itself -- if the filter
|
||||
denies `openat2(2)` then we would return that error rather than falling back
|
||||
to the `O_PATH` resolver. To resolve this issue, we no longer cache the
|
||||
result if `openat2(2)` was successful, only if there was an error.
|
||||
- A file descriptor leak in our `openat2` wrapper (when doing the necessary
|
||||
`dup` for `RESOLVE_IN_ROOT`) has been removed.
|
||||
|
||||
## [0.5.2] - 2025-11-19 ##
|
||||
|
||||
> "Will you walk into my parlour?" said a spider to a fly.
|
||||
|
||||
### Fixed ###
|
||||
- Our logic for deciding whether to use `openat2(2)` or fallback to an `O_PATH`
|
||||
resolver would cache the result to avoid doing needless test runs of
|
||||
`openat2(2)`. However, this causes issues when `pathrs-lite` is being used by
|
||||
a program that applies new seccomp-bpf filters onto itself -- if the filter
|
||||
denies `openat2(2)` then we would return that error rather than falling back
|
||||
to the `O_PATH` resolver. To resolve this issue, we no longer cache the
|
||||
result if `openat2(2)` was successful, only if there was an error.
|
||||
- A file descriptor leak in our `openat2` wrapper (when doing the necessary
|
||||
`dup` for `RESOLVE_IN_ROOT`) has been removed.
|
||||
|
||||
## [0.6.0] - 2025-11-03 ##
|
||||
|
||||
> By the Power of Greyskull!
|
||||
|
||||
### Breaking ###
|
||||
- The deprecated `MkdirAll`, `MkdirAllHandle`, `OpenInRoot`, `OpenatInRoot` and
|
||||
`Reopen` wrappers have been removed. Please switch to using `pathrs-lite`
|
||||
directly.
|
||||
|
||||
### Added ###
|
||||
- `pathrs-lite` now has support for using libpathrs as a backend. This is
|
||||
opt-in and can be enabled at build time with the `libpathrs` build tag. The
|
||||
intention is to allow for downstream libraries and other projects to make use
|
||||
of the pure-Go `github.com/cyphar/filepath-securejoin/pathrs-lite` package
|
||||
and distributors can then opt-in to using `libpathrs` for the entire binary
|
||||
if they wish.
|
||||
|
||||
## [0.5.1] - 2025-10-31 ##
|
||||
|
||||
> Spooky scary skeletons send shivers down your spine!
|
||||
|
||||
### Changed ###
|
||||
- `openat2` can return `-EAGAIN` if it detects a possible attack in certain
|
||||
scenarios (namely if there was a rename or mount while walking a path with a
|
||||
`..` component). While this is necessary to avoid a denial-of-service in the
|
||||
kernel, it does require retry loops in userspace.
|
||||
|
||||
In previous versions, `pathrs-lite` would retry `openat2` 32 times before
|
||||
returning an error, but we've received user reports that this limit can be
|
||||
hit on systems with very heavy load. In some synthetic benchmarks (testing
|
||||
the worst-case of an attacker doing renames in a tight loop on every core of
|
||||
a 16-core machine) we managed to get a ~3% failure rate in runc. We have
|
||||
improved this situation in two ways:
|
||||
|
||||
* We have now increased this limit to 128, which should be good enough for
|
||||
most use-cases without becoming a denial-of-service vector (the number of
|
||||
syscalls called by the `O_PATH` resolver in a typical case is within the
|
||||
same ballpark). The same benchmarks show a failure rate of ~0.12% which
|
||||
(while not zero) is probably sufficient for most users.
|
||||
|
||||
* In addition, we now return a `unix.EAGAIN` error that is bubbled up and can
|
||||
be detected by callers. This means that callers with stricter requirements
|
||||
to avoid spurious errors can choose to do their own infinite `EAGAIN` retry
|
||||
loop (though we would strongly recommend users use time-based deadlines in
|
||||
such retry loops to avoid potentially unbounded denials-of-service).
|
||||
|
||||
## [0.5.0] - 2025-09-26 ##
|
||||
|
||||
> Let the past die. Kill it if you have to.
|
||||
@ -430,11 +354,7 @@ This is our first release of `github.com/cyphar/filepath-securejoin`,
|
||||
containing a full implementation with a coverage of 93.5% (the only missing
|
||||
cases are the error cases, which are hard to mocktest at the moment).
|
||||
|
||||
[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.6.1...HEAD
|
||||
[0.6.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.6.0...v0.6.1
|
||||
[0.6.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.5.0...v0.6.0
|
||||
[0.5.2]: https://github.com/cyphar/filepath-securejoin/compare/v0.5.1...v0.5.2
|
||||
[0.5.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.5.0...v0.5.1
|
||||
[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.5.0...HEAD
|
||||
[0.5.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.1...v0.5.0
|
||||
[0.4.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.0...v0.4.1
|
||||
[0.4.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...v0.4.0
|
||||
|
||||
2
vendor/github.com/cyphar/filepath-securejoin/VERSION
generated
vendored
2
vendor/github.com/cyphar/filepath-securejoin/VERSION
generated
vendored
@ -1 +1 @@
|
||||
0.6.1
|
||||
0.5.0
|
||||
|
||||
48
vendor/github.com/cyphar/filepath-securejoin/deprecated_linux.go
generated
vendored
Normal file
48
vendor/github.com/cyphar/filepath-securejoin/deprecated_linux.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package securejoin
|
||||
|
||||
import (
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite"
|
||||
)
|
||||
|
||||
var (
|
||||
// MkdirAll is a wrapper around [pathrs.MkdirAll].
|
||||
//
|
||||
// Deprecated: You should use [pathrs.MkdirAll] directly instead. This
|
||||
// wrapper will be removed in filepath-securejoin v0.6.
|
||||
MkdirAll = pathrs.MkdirAll
|
||||
|
||||
// MkdirAllHandle is a wrapper around [pathrs.MkdirAllHandle].
|
||||
//
|
||||
// Deprecated: You should use [pathrs.MkdirAllHandle] directly instead.
|
||||
// This wrapper will be removed in filepath-securejoin v0.6.
|
||||
MkdirAllHandle = pathrs.MkdirAllHandle
|
||||
|
||||
// OpenInRoot is a wrapper around [pathrs.OpenInRoot].
|
||||
//
|
||||
// Deprecated: You should use [pathrs.OpenInRoot] directly instead. This
|
||||
// wrapper will be removed in filepath-securejoin v0.6.
|
||||
OpenInRoot = pathrs.OpenInRoot
|
||||
|
||||
// OpenatInRoot is a wrapper around [pathrs.OpenatInRoot].
|
||||
//
|
||||
// Deprecated: You should use [pathrs.OpenatInRoot] directly instead. This
|
||||
// wrapper will be removed in filepath-securejoin v0.6.
|
||||
OpenatInRoot = pathrs.OpenatInRoot
|
||||
|
||||
// Reopen is a wrapper around [pathrs.Reopen].
|
||||
//
|
||||
// Deprecated: You should use [pathrs.Reopen] directly instead. This
|
||||
// wrapper will be removed in filepath-securejoin v0.6.
|
||||
Reopen = pathrs.Reopen
|
||||
)
|
||||
33
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/README.md
generated
vendored
Normal file
33
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/README.md
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
## `pathrs-lite` ##
|
||||
|
||||
`github.com/cyphar/filepath-securejoin/pathrs-lite` provides a minimal **pure
|
||||
Go** implementation of the core bits of [libpathrs][]. This is not intended to
|
||||
be a complete replacement for libpathrs, instead it is mainly intended to be
|
||||
useful as a transition tool for existing Go projects.
|
||||
|
||||
The long-term plan for `pathrs-lite` is to provide a build tag that will cause
|
||||
all `pathrs-lite` operations to call into libpathrs directly, thus removing
|
||||
code duplication for projects that wish to make use of libpathrs (and providing
|
||||
the ability for software packagers to opt-in to libpathrs support without
|
||||
needing to patch upstream).
|
||||
|
||||
[libpathrs]: https://github.com/cyphar/libpathrs
|
||||
|
||||
### License ###
|
||||
|
||||
Most of this subpackage is licensed under the Mozilla Public License (version
|
||||
2.0). For more information, see the top-level [COPYING.md][] and
|
||||
[LICENSE.MPL-2.0][] files, as well as the individual license headers for each
|
||||
file.
|
||||
|
||||
```
|
||||
Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
Copyright (C) 2024-2025 SUSE LLC
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
```
|
||||
|
||||
[COPYING.md]: ../COPYING.md
|
||||
[LICENSE.MPL-2.0]: ../LICENSE.MPL-2.0
|
||||
14
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/doc.go
generated
vendored
Normal file
14
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/doc.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package pathrs (pathrs-lite) is a less complete pure Go implementation of
|
||||
// some of the APIs provided by [libpathrs].
|
||||
package pathrs
|
||||
30
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/assert/assert.go
generated
vendored
Normal file
30
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/assert/assert.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Copyright (C) 2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package assert provides some basic assertion helpers for Go.
|
||||
package assert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Assert panics if the predicate is false with the provided argument.
|
||||
func Assert(predicate bool, msg any) {
|
||||
if !predicate {
|
||||
panic(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Assertf panics if the predicate is false and formats the message using the
|
||||
// same formatting as [fmt.Printf].
|
||||
//
|
||||
// [fmt.Printf]: https://pkg.go.dev/fmt#Printf
|
||||
func Assertf(predicate bool, fmtMsg string, args ...any) {
|
||||
Assert(predicate, fmt.Sprintf(fmtMsg, args...))
|
||||
}
|
||||
30
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/errors.go
generated
vendored
Normal file
30
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/errors.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package internal contains unexported common code for filepath-securejoin.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrPossibleAttack indicates that some attack was detected.
|
||||
ErrPossibleAttack = errors.New("possible attack detected")
|
||||
|
||||
// ErrPossibleBreakout indicates that during an operation we ended up in a
|
||||
// state that could be a breakout but we detected it.
|
||||
ErrPossibleBreakout = errors.New("possible breakout detected")
|
||||
|
||||
// ErrInvalidDirectory indicates an unlinked directory.
|
||||
ErrInvalidDirectory = errors.New("wandered into deleted directory")
|
||||
|
||||
// ErrDeletedInode indicates an unlinked file (non-directory).
|
||||
ErrDeletedInode = errors.New("cannot verify path of deleted inode")
|
||||
)
|
||||
148
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/at_linux.go
generated
vendored
Normal file
148
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/at_linux.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package fd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
|
||||
)
|
||||
|
||||
// prepareAtWith returns -EBADF (an invalid fd) if dir is nil, otherwise using
|
||||
// the dir.Fd(). We use -EBADF because in filepath-securejoin we generally
|
||||
// don't want to allow relative-to-cwd paths. The returned path is an
|
||||
// *informational* string that describes a reasonable pathname for the given
|
||||
// *at(2) arguments. You must not use the full path for any actual filesystem
|
||||
// operations.
|
||||
func prepareAt(dir Fd, path string) (dirFd int, unsafeUnmaskedPath string) {
|
||||
dirFd, dirPath := -int(unix.EBADF), "."
|
||||
if dir != nil {
|
||||
dirFd, dirPath = int(dir.Fd()), dir.Name()
|
||||
}
|
||||
if !filepath.IsAbs(path) {
|
||||
// only prepend the dirfd path for relative paths
|
||||
path = dirPath + "/" + path
|
||||
}
|
||||
// NOTE: If path is "." or "", the returned path won't be filepath.Clean,
|
||||
// but that's okay since this path is either used for errors (in which case
|
||||
// a trailing "/" or "/." is important information) or will be
|
||||
// filepath.Clean'd later (in the case of fd.Openat).
|
||||
return dirFd, path
|
||||
}
|
||||
|
||||
// Openat is an [Fd]-based wrapper around unix.Openat.
|
||||
func Openat(dir Fd, path string, flags int, mode int) (*os.File, error) { //nolint:unparam // wrapper func
|
||||
dirFd, fullPath := prepareAt(dir, path)
|
||||
// Make sure we always set O_CLOEXEC.
|
||||
flags |= unix.O_CLOEXEC
|
||||
fd, err := unix.Openat(dirFd, path, flags, uint32(mode))
|
||||
if err != nil {
|
||||
return nil, &os.PathError{Op: "openat", Path: fullPath, Err: err}
|
||||
}
|
||||
runtime.KeepAlive(dir)
|
||||
// openat is only used with lexically-safe paths so we can use
|
||||
// filepath.Clean here, and also the path itself is not going to be used
|
||||
// for actual path operations.
|
||||
fullPath = filepath.Clean(fullPath)
|
||||
return os.NewFile(uintptr(fd), fullPath), nil
|
||||
}
|
||||
|
||||
// Fstatat is an [Fd]-based wrapper around unix.Fstatat.
|
||||
func Fstatat(dir Fd, path string, flags int) (unix.Stat_t, error) {
|
||||
dirFd, fullPath := prepareAt(dir, path)
|
||||
var stat unix.Stat_t
|
||||
if err := unix.Fstatat(dirFd, path, &stat, flags); err != nil {
|
||||
return stat, &os.PathError{Op: "fstatat", Path: fullPath, Err: err}
|
||||
}
|
||||
runtime.KeepAlive(dir)
|
||||
return stat, nil
|
||||
}
|
||||
|
||||
// Faccessat is an [Fd]-based wrapper around unix.Faccessat.
|
||||
func Faccessat(dir Fd, path string, mode uint32, flags int) error {
|
||||
dirFd, fullPath := prepareAt(dir, path)
|
||||
err := unix.Faccessat(dirFd, path, mode, flags)
|
||||
if err != nil {
|
||||
err = &os.PathError{Op: "faccessat", Path: fullPath, Err: err}
|
||||
}
|
||||
runtime.KeepAlive(dir)
|
||||
return err
|
||||
}
|
||||
|
||||
// Readlinkat is an [Fd]-based wrapper around unix.Readlinkat.
|
||||
func Readlinkat(dir Fd, path string) (string, error) {
|
||||
dirFd, fullPath := prepareAt(dir, path)
|
||||
size := 4096
|
||||
for {
|
||||
linkBuf := make([]byte, size)
|
||||
n, err := unix.Readlinkat(dirFd, path, linkBuf)
|
||||
if err != nil {
|
||||
return "", &os.PathError{Op: "readlinkat", Path: fullPath, Err: err}
|
||||
}
|
||||
runtime.KeepAlive(dir)
|
||||
if n != size {
|
||||
return string(linkBuf[:n]), nil
|
||||
}
|
||||
// Possible truncation, resize the buffer.
|
||||
size *= 2
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// STATX_MNT_ID_UNIQUE is provided in golang.org/x/sys@v0.20.0, but in order to
|
||||
// avoid bumping the requirement for a single constant we can just define it
|
||||
// ourselves.
|
||||
_STATX_MNT_ID_UNIQUE = 0x4000 //nolint:revive // unix.* name
|
||||
|
||||
// We don't care which mount ID we get. The kernel will give us the unique
|
||||
// one if it is supported. If the kernel doesn't support
|
||||
// STATX_MNT_ID_UNIQUE, the bit is ignored and the returned request mask
|
||||
// will only contain STATX_MNT_ID (if supported).
|
||||
wantStatxMntMask = _STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID
|
||||
)
|
||||
|
||||
var hasStatxMountID = gocompat.SyncOnceValue(func() bool {
|
||||
var stx unix.Statx_t
|
||||
err := unix.Statx(-int(unix.EBADF), "/", 0, wantStatxMntMask, &stx)
|
||||
return err == nil && stx.Mask&wantStatxMntMask != 0
|
||||
})
|
||||
|
||||
// GetMountID gets the mount identifier associated with the fd and path
|
||||
// combination. It is effectively a wrapper around fetching
|
||||
// STATX_MNT_ID{,_UNIQUE} with unix.Statx, but with a fallback to 0 if the
|
||||
// kernel doesn't support the feature.
|
||||
func GetMountID(dir Fd, path string) (uint64, error) {
|
||||
// If we don't have statx(STATX_MNT_ID*) support, we can't do anything.
|
||||
if !hasStatxMountID() {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
dirFd, fullPath := prepareAt(dir, path)
|
||||
|
||||
var stx unix.Statx_t
|
||||
err := unix.Statx(dirFd, path, unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW, wantStatxMntMask, &stx)
|
||||
if stx.Mask&wantStatxMntMask == 0 {
|
||||
// It's not a kernel limitation, for some reason we couldn't get a
|
||||
// mount ID. Assume it's some kind of attack.
|
||||
err = fmt.Errorf("could not get mount id: %w", err)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, &os.PathError{Op: "statx(STATX_MNT_ID_...)", Path: fullPath, Err: err}
|
||||
}
|
||||
runtime.KeepAlive(dir)
|
||||
return stx.Mnt_id, nil
|
||||
}
|
||||
55
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/fd.go
generated
vendored
Normal file
55
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/fd.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Copyright (C) 2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package fd provides a drop-in interface-based replacement of [*os.File] that
|
||||
// allows for things like noop-Close wrappers to be used.
|
||||
//
|
||||
// [*os.File]: https://pkg.go.dev/os#File
|
||||
package fd
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Fd is an interface that mirrors most of the API of [*os.File], allowing you
|
||||
// to create wrappers that can be used in place of [*os.File].
|
||||
//
|
||||
// [*os.File]: https://pkg.go.dev/os#File
|
||||
type Fd interface {
|
||||
io.Closer
|
||||
Name() string
|
||||
Fd() uintptr
|
||||
}
|
||||
|
||||
// Compile-time interface checks.
|
||||
var (
|
||||
_ Fd = (*os.File)(nil)
|
||||
_ Fd = noClose{}
|
||||
)
|
||||
|
||||
type noClose struct{ inner Fd }
|
||||
|
||||
func (f noClose) Name() string { return f.inner.Name() }
|
||||
func (f noClose) Fd() uintptr { return f.inner.Fd() }
|
||||
|
||||
func (f noClose) Close() error { return nil }
|
||||
|
||||
// NopCloser returns an [*os.File]-like object where the [Close] method is now
|
||||
// a no-op.
|
||||
//
|
||||
// Note that for [*os.File] and similar objects, the Go garbage collector will
|
||||
// still call [Close] on the underlying file unless you use
|
||||
// [runtime.SetFinalizer] to disable this behaviour. This is up to the caller
|
||||
// to do (if necessary).
|
||||
//
|
||||
// [*os.File]: https://pkg.go.dev/os#File
|
||||
// [Close]: https://pkg.go.dev/io#Closer
|
||||
// [runtime.SetFinalizer]: https://pkg.go.dev/runtime#SetFinalizer
|
||||
func NopCloser(f Fd) Fd { return noClose{inner: f} }
|
||||
78
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/fd_linux.go
generated
vendored
Normal file
78
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/fd_linux.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package fd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal"
|
||||
)
|
||||
|
||||
// DupWithName creates a new file descriptor referencing the same underlying
|
||||
// file, but with the provided name instead of fd.Name().
|
||||
func DupWithName(fd Fd, name string) (*os.File, error) {
|
||||
fd2, err := unix.FcntlInt(fd.Fd(), unix.F_DUPFD_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("fcntl(F_DUPFD_CLOEXEC)", err)
|
||||
}
|
||||
runtime.KeepAlive(fd)
|
||||
return os.NewFile(uintptr(fd2), name), nil
|
||||
}
|
||||
|
||||
// Dup creates a new file description referencing the same underlying file.
|
||||
func Dup(fd Fd) (*os.File, error) {
|
||||
return DupWithName(fd, fd.Name())
|
||||
}
|
||||
|
||||
// Fstat is an [Fd]-based wrapper around unix.Fstat.
|
||||
func Fstat(fd Fd) (unix.Stat_t, error) {
|
||||
var stat unix.Stat_t
|
||||
if err := unix.Fstat(int(fd.Fd()), &stat); err != nil {
|
||||
return stat, &os.PathError{Op: "fstat", Path: fd.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(fd)
|
||||
return stat, nil
|
||||
}
|
||||
|
||||
// Fstatfs is an [Fd]-based wrapper around unix.Fstatfs.
|
||||
func Fstatfs(fd Fd) (unix.Statfs_t, error) {
|
||||
var statfs unix.Statfs_t
|
||||
if err := unix.Fstatfs(int(fd.Fd()), &statfs); err != nil {
|
||||
return statfs, &os.PathError{Op: "fstatfs", Path: fd.Name(), Err: err}
|
||||
}
|
||||
runtime.KeepAlive(fd)
|
||||
return statfs, nil
|
||||
}
|
||||
|
||||
// IsDeadInode detects whether the file has been unlinked from a filesystem and
|
||||
// is thus a "dead inode" from the kernel's perspective.
|
||||
func IsDeadInode(file Fd) error {
|
||||
// If the nlink of a file drops to 0, there is an attacker deleting
|
||||
// directories during our walk, which could result in weird /proc values.
|
||||
// It's better to error out in this case.
|
||||
stat, err := Fstat(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("check for dead inode: %w", err)
|
||||
}
|
||||
if stat.Nlink == 0 {
|
||||
err := internal.ErrDeletedInode
|
||||
if stat.Mode&unix.S_IFMT == unix.S_IFDIR {
|
||||
err = internal.ErrInvalidDirectory
|
||||
}
|
||||
return fmt.Errorf("%w %q", err, file.Name())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
54
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/mount_linux.go
generated
vendored
Normal file
54
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/mount_linux.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package fd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Fsopen is an [Fd]-based wrapper around unix.Fsopen.
|
||||
func Fsopen(fsName string, flags int) (*os.File, error) {
|
||||
// Make sure we always set O_CLOEXEC.
|
||||
flags |= unix.FSOPEN_CLOEXEC
|
||||
fd, err := unix.Fsopen(fsName, flags)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("fsopen "+fsName, err)
|
||||
}
|
||||
return os.NewFile(uintptr(fd), "fscontext:"+fsName), nil
|
||||
}
|
||||
|
||||
// Fsmount is an [Fd]-based wrapper around unix.Fsmount.
|
||||
func Fsmount(ctx Fd, flags, mountAttrs int) (*os.File, error) {
|
||||
// Make sure we always set O_CLOEXEC.
|
||||
flags |= unix.FSMOUNT_CLOEXEC
|
||||
fd, err := unix.Fsmount(int(ctx.Fd()), flags, mountAttrs)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("fsmount "+ctx.Name(), err)
|
||||
}
|
||||
return os.NewFile(uintptr(fd), "fsmount:"+ctx.Name()), nil
|
||||
}
|
||||
|
||||
// OpenTree is an [Fd]-based wrapper around unix.OpenTree.
|
||||
func OpenTree(dir Fd, path string, flags uint) (*os.File, error) {
|
||||
dirFd, fullPath := prepareAt(dir, path)
|
||||
// Make sure we always set O_CLOEXEC.
|
||||
flags |= unix.OPEN_TREE_CLOEXEC
|
||||
fd, err := unix.OpenTree(dirFd, path, flags)
|
||||
if err != nil {
|
||||
return nil, &os.PathError{Op: "open_tree", Path: fullPath, Err: err}
|
||||
}
|
||||
runtime.KeepAlive(dir)
|
||||
return os.NewFile(uintptr(fd), fullPath), nil
|
||||
}
|
||||
62
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/openat2_linux.go
generated
vendored
Normal file
62
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/openat2_linux.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package fd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal"
|
||||
)
|
||||
|
||||
func scopedLookupShouldRetry(how *unix.OpenHow, err error) bool {
|
||||
// RESOLVE_IN_ROOT (and RESOLVE_BENEATH) can return -EAGAIN if we resolve
|
||||
// ".." while a mount or rename occurs anywhere on the system. This could
|
||||
// happen spuriously, or as the result of an attacker trying to mess with
|
||||
// us during lookup.
|
||||
//
|
||||
// In addition, scoped lookups have a "safety check" at the end of
|
||||
// complete_walk which will return -EXDEV if the final path is not in the
|
||||
// root.
|
||||
return how.Resolve&(unix.RESOLVE_IN_ROOT|unix.RESOLVE_BENEATH) != 0 &&
|
||||
(errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EXDEV))
|
||||
}
|
||||
|
||||
const scopedLookupMaxRetries = 32
|
||||
|
||||
// Openat2 is an [Fd]-based wrapper around unix.Openat2, but with some retry
|
||||
// logic in case of EAGAIN errors.
|
||||
func Openat2(dir Fd, path string, how *unix.OpenHow) (*os.File, error) {
|
||||
dirFd, fullPath := prepareAt(dir, path)
|
||||
// Make sure we always set O_CLOEXEC.
|
||||
how.Flags |= unix.O_CLOEXEC
|
||||
var tries int
|
||||
for tries < scopedLookupMaxRetries {
|
||||
fd, err := unix.Openat2(dirFd, path, how)
|
||||
if err != nil {
|
||||
if scopedLookupShouldRetry(how, err) {
|
||||
// We retry a couple of times to avoid the spurious errors, and
|
||||
// if we are being attacked then returning -EAGAIN is the best
|
||||
// we can do.
|
||||
tries++
|
||||
continue
|
||||
}
|
||||
return nil, &os.PathError{Op: "openat2", Path: fullPath, Err: err}
|
||||
}
|
||||
runtime.KeepAlive(dir)
|
||||
return os.NewFile(uintptr(fd), fullPath), nil
|
||||
}
|
||||
return nil, &os.PathError{Op: "openat2", Path: fullPath, Err: internal.ErrPossibleAttack}
|
||||
}
|
||||
10
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/README.md
generated
vendored
Normal file
10
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/README.md
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
## gocompat ##
|
||||
|
||||
This directory contains backports of stdlib functions from later Go versions so
|
||||
the filepath-securejoin can continue to be used by projects that are stuck with
|
||||
Go 1.18 support. Note that often filepath-securejoin is added in security
|
||||
patches for old releases, so avoiding the need to bump Go compiler requirements
|
||||
is a huge plus to downstreams.
|
||||
|
||||
The source code is licensed under the same license as the Go stdlib. See the
|
||||
source files for the precise license information.
|
||||
13
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/doc.go
generated
vendored
Normal file
13
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/doc.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build linux && go1.20
|
||||
|
||||
// Copyright (C) 2025 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package gocompat includes compatibility shims (backported from future Go
|
||||
// stdlib versions) to permit filepath-securejoin to be used with older Go
|
||||
// versions (often filepath-securejoin is added in security patches for old
|
||||
// releases, so avoiding the need to bump Go compiler requirements is a huge
|
||||
// plus to downstreams).
|
||||
package gocompat
|
||||
19
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_errors_go120.go
generated
vendored
Normal file
19
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_errors_go120.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build linux && go1.20
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gocompat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// WrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
|
||||
// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
|
||||
// is only guaranteed to give you baseErr.
|
||||
func WrapBaseError(baseErr, extraErr error) error {
|
||||
return fmt.Errorf("%w: %w", extraErr, baseErr)
|
||||
}
|
||||
40
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_errors_unsupported.go
generated
vendored
Normal file
40
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_errors_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build linux && !go1.20
|
||||
|
||||
// Copyright (C) 2024 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gocompat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type wrappedError struct {
|
||||
inner error
|
||||
isError error
|
||||
}
|
||||
|
||||
func (err wrappedError) Is(target error) bool {
|
||||
return err.isError == target
|
||||
}
|
||||
|
||||
func (err wrappedError) Unwrap() error {
|
||||
return err.inner
|
||||
}
|
||||
|
||||
func (err wrappedError) Error() string {
|
||||
return fmt.Sprintf("%v: %v", err.isError, err.inner)
|
||||
}
|
||||
|
||||
// WrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
|
||||
// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
|
||||
// is only guaranteed to give you baseErr.
|
||||
func WrapBaseError(baseErr, extraErr error) error {
|
||||
return wrappedError{
|
||||
inner: baseErr,
|
||||
isError: extraErr,
|
||||
}
|
||||
}
|
||||
53
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_generics_go121.go
generated
vendored
Normal file
53
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_generics_go121.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build linux && go1.21
|
||||
|
||||
// Copyright (C) 2024-2025 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gocompat
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"slices"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// SlicesDeleteFunc is equivalent to Go 1.21's slices.DeleteFunc.
|
||||
func SlicesDeleteFunc[S ~[]E, E any](slice S, delFn func(E) bool) S {
|
||||
return slices.DeleteFunc(slice, delFn)
|
||||
}
|
||||
|
||||
// SlicesContains is equivalent to Go 1.21's slices.Contains.
|
||||
func SlicesContains[S ~[]E, E comparable](slice S, val E) bool {
|
||||
return slices.Contains(slice, val)
|
||||
}
|
||||
|
||||
// SlicesClone is equivalent to Go 1.21's slices.Clone.
|
||||
func SlicesClone[S ~[]E, E any](slice S) S {
|
||||
return slices.Clone(slice)
|
||||
}
|
||||
|
||||
// SyncOnceValue is equivalent to Go 1.21's sync.OnceValue.
|
||||
func SyncOnceValue[T any](f func() T) func() T {
|
||||
return sync.OnceValue(f)
|
||||
}
|
||||
|
||||
// SyncOnceValues is equivalent to Go 1.21's sync.OnceValues.
|
||||
func SyncOnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
|
||||
return sync.OnceValues(f)
|
||||
}
|
||||
|
||||
// CmpOrdered is equivalent to Go 1.21's cmp.Ordered generic type definition.
|
||||
type CmpOrdered = cmp.Ordered
|
||||
|
||||
// CmpCompare is equivalent to Go 1.21's cmp.Compare.
|
||||
func CmpCompare[T CmpOrdered](x, y T) int {
|
||||
return cmp.Compare(x, y)
|
||||
}
|
||||
|
||||
// Max2 is equivalent to Go 1.21's max builtin (but only for two parameters).
|
||||
func Max2[T CmpOrdered](x, y T) T {
|
||||
return max(x, y)
|
||||
}
|
||||
187
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_generics_unsupported.go
generated
vendored
Normal file
187
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_generics_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build linux && !go1.21
|
||||
|
||||
// Copyright (C) 2021, 2022 The Go Authors. All rights reserved.
|
||||
// Copyright (C) 2024-2025 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE.BSD file.
|
||||
|
||||
package gocompat
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// These are very minimal implementations of functions that appear in Go 1.21's
|
||||
// stdlib, included so that we can build on older Go versions. Most are
|
||||
// borrowed directly from the stdlib, and a few are modified to be "obviously
|
||||
// correct" without needing to copy too many other helpers.
|
||||
|
||||
// clearSlice is equivalent to Go 1.21's builtin clear.
|
||||
// Copied from the Go 1.24 stdlib implementation.
|
||||
func clearSlice[S ~[]E, E any](slice S) {
|
||||
var zero E
|
||||
for i := range slice {
|
||||
slice[i] = zero
|
||||
}
|
||||
}
|
||||
|
||||
// slicesIndexFunc is equivalent to Go 1.21's slices.IndexFunc.
|
||||
// Copied from the Go 1.24 stdlib implementation.
|
||||
func slicesIndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
|
||||
for i := range s {
|
||||
if f(s[i]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// SlicesDeleteFunc is equivalent to Go 1.21's slices.DeleteFunc.
|
||||
// Copied from the Go 1.24 stdlib implementation.
|
||||
func SlicesDeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
|
||||
i := slicesIndexFunc(s, del)
|
||||
if i == -1 {
|
||||
return s
|
||||
}
|
||||
// Don't start copying elements until we find one to delete.
|
||||
for j := i + 1; j < len(s); j++ {
|
||||
if v := s[j]; !del(v) {
|
||||
s[i] = v
|
||||
i++
|
||||
}
|
||||
}
|
||||
clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
// SlicesContains is equivalent to Go 1.21's slices.Contains.
|
||||
// Similar to the stdlib slices.Contains, except that we don't have
|
||||
// slices.Index so we need to use slices.IndexFunc for this non-Func helper.
|
||||
func SlicesContains[S ~[]E, E comparable](s S, v E) bool {
|
||||
return slicesIndexFunc(s, func(e E) bool { return e == v }) >= 0
|
||||
}
|
||||
|
||||
// SlicesClone is equivalent to Go 1.21's slices.Clone.
|
||||
// Copied from the Go 1.24 stdlib implementation.
|
||||
func SlicesClone[S ~[]E, E any](s S) S {
|
||||
// Preserve nil in case it matters.
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return append(S([]E{}), s...)
|
||||
}
|
||||
|
||||
// SyncOnceValue is equivalent to Go 1.21's sync.OnceValue.
|
||||
// Copied from the Go 1.25 stdlib implementation.
|
||||
func SyncOnceValue[T any](f func() T) func() T {
|
||||
// Use a struct so that there's a single heap allocation.
|
||||
d := struct {
|
||||
f func() T
|
||||
once sync.Once
|
||||
valid bool
|
||||
p any
|
||||
result T
|
||||
}{
|
||||
f: f,
|
||||
}
|
||||
return func() T {
|
||||
d.once.Do(func() {
|
||||
defer func() {
|
||||
d.f = nil
|
||||
d.p = recover()
|
||||
if !d.valid {
|
||||
panic(d.p)
|
||||
}
|
||||
}()
|
||||
d.result = d.f()
|
||||
d.valid = true
|
||||
})
|
||||
if !d.valid {
|
||||
panic(d.p)
|
||||
}
|
||||
return d.result
|
||||
}
|
||||
}
|
||||
|
||||
// SyncOnceValues is equivalent to Go 1.21's sync.OnceValues.
|
||||
// Copied from the Go 1.25 stdlib implementation.
|
||||
func SyncOnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
|
||||
// Use a struct so that there's a single heap allocation.
|
||||
d := struct {
|
||||
f func() (T1, T2)
|
||||
once sync.Once
|
||||
valid bool
|
||||
p any
|
||||
r1 T1
|
||||
r2 T2
|
||||
}{
|
||||
f: f,
|
||||
}
|
||||
return func() (T1, T2) {
|
||||
d.once.Do(func() {
|
||||
defer func() {
|
||||
d.f = nil
|
||||
d.p = recover()
|
||||
if !d.valid {
|
||||
panic(d.p)
|
||||
}
|
||||
}()
|
||||
d.r1, d.r2 = d.f()
|
||||
d.valid = true
|
||||
})
|
||||
if !d.valid {
|
||||
panic(d.p)
|
||||
}
|
||||
return d.r1, d.r2
|
||||
}
|
||||
}
|
||||
|
||||
// CmpOrdered is equivalent to Go 1.21's cmp.Ordered generic type definition.
|
||||
// Copied from the Go 1.25 stdlib implementation.
|
||||
type CmpOrdered interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 |
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
|
||||
~float32 | ~float64 |
|
||||
~string
|
||||
}
|
||||
|
||||
// isNaN reports whether x is a NaN without requiring the math package.
|
||||
// This will always return false if T is not floating-point.
|
||||
// Copied from the Go 1.25 stdlib implementation.
|
||||
func isNaN[T CmpOrdered](x T) bool {
|
||||
return x != x
|
||||
}
|
||||
|
||||
// CmpCompare is equivalent to Go 1.21's cmp.Compare.
|
||||
// Copied from the Go 1.25 stdlib implementation.
|
||||
func CmpCompare[T CmpOrdered](x, y T) int {
|
||||
xNaN := isNaN(x)
|
||||
yNaN := isNaN(y)
|
||||
if xNaN {
|
||||
if yNaN {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
if yNaN {
|
||||
return +1
|
||||
}
|
||||
if x < y {
|
||||
return -1
|
||||
}
|
||||
if x > y {
|
||||
return +1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Max2 is equivalent to Go 1.21's max builtin for two parameters.
|
||||
func Max2[T CmpOrdered](x, y T) T {
|
||||
m := x
|
||||
if y > m {
|
||||
m = y
|
||||
}
|
||||
return m
|
||||
}
|
||||
123
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/kernelversion/kernel_linux.go
generated
vendored
Normal file
123
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/kernelversion/kernel_linux.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Copyright (C) 2022 The Go Authors. All rights reserved.
|
||||
// Copyright (C) 2025 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE.BSD file.
|
||||
|
||||
// The parsing logic is very loosely based on the Go stdlib's
|
||||
// src/internal/syscall/unix/kernel_version_linux.go but with an API that looks
|
||||
// a bit like runc's libcontainer/system/kernelversion.
|
||||
//
|
||||
// TODO(cyphar): This API has been copied around to a lot of different projects
|
||||
// (Docker, containerd, runc, and now filepath-securejoin) -- maybe we should
|
||||
// put it in a separate project?
|
||||
|
||||
// Package kernelversion provides a simple mechanism for checking whether the
|
||||
// running kernel is at least as new as some baseline kernel version. This is
|
||||
// often useful when checking for features that would be too complicated to
|
||||
// test support for (or in cases where we know that some kernel features in
|
||||
// backport-heavy kernels are broken and need to be avoided).
|
||||
package kernelversion
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
|
||||
)
|
||||
|
||||
// KernelVersion is a numeric representation of the key numerical elements of a
|
||||
// kernel version (for instance, "4.1.2-default-1" would be represented as
|
||||
// KernelVersion{4, 1, 2}).
|
||||
type KernelVersion []uint64
|
||||
|
||||
func (kver KernelVersion) String() string {
|
||||
var str strings.Builder
|
||||
for idx, elem := range kver {
|
||||
if idx != 0 {
|
||||
_, _ = str.WriteRune('.')
|
||||
}
|
||||
_, _ = str.WriteString(strconv.FormatUint(elem, 10))
|
||||
}
|
||||
return str.String()
|
||||
}
|
||||
|
||||
var errInvalidKernelVersion = errors.New("invalid kernel version")
|
||||
|
||||
// parseKernelVersion parses a string and creates a KernelVersion based on it.
|
||||
func parseKernelVersion(kverStr string) (KernelVersion, error) {
|
||||
kver := make(KernelVersion, 1, 3)
|
||||
for idx, ch := range kverStr {
|
||||
if '0' <= ch && ch <= '9' {
|
||||
v := &kver[len(kver)-1]
|
||||
*v = (*v * 10) + uint64(ch-'0')
|
||||
} else {
|
||||
if idx == 0 || kverStr[idx-1] < '0' || '9' < kverStr[idx-1] {
|
||||
// "." must be preceded by a digit while in version section
|
||||
return nil, fmt.Errorf("%w %q: kernel version has dot(s) followed by non-digit in version section", errInvalidKernelVersion, kverStr)
|
||||
}
|
||||
if ch != '.' {
|
||||
break
|
||||
}
|
||||
kver = append(kver, 0)
|
||||
}
|
||||
}
|
||||
if len(kver) < 2 {
|
||||
return nil, fmt.Errorf("%w %q: kernel versions must contain at least two components", errInvalidKernelVersion, kverStr)
|
||||
}
|
||||
return kver, nil
|
||||
}
|
||||
|
||||
// getKernelVersion gets the current kernel version.
|
||||
var getKernelVersion = gocompat.SyncOnceValues(func() (KernelVersion, error) {
|
||||
var uts unix.Utsname
|
||||
if err := unix.Uname(&uts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Remove the \x00 from the release.
|
||||
release := uts.Release[:]
|
||||
return parseKernelVersion(string(release[:bytes.IndexByte(release, 0)]))
|
||||
})
|
||||
|
||||
// GreaterEqualThan returns true if the the host kernel version is greater than
|
||||
// or equal to the provided [KernelVersion]. When doing this comparison, any
|
||||
// non-numerical suffixes of the host kernel version are ignored.
|
||||
//
|
||||
// If the number of components provided is not equal to the number of numerical
|
||||
// components of the host kernel version, any missing components are treated as
|
||||
// 0. This means that GreaterEqualThan(KernelVersion{4}) will be treated the
|
||||
// same as GreaterEqualThan(KernelVersion{4, 0, 0, ..., 0, 0}), and that if the
|
||||
// host kernel version is "4" then GreaterEqualThan(KernelVersion{4, 1}) will
|
||||
// return false (because the host version will be treated as "4.0").
|
||||
func GreaterEqualThan(wantKver KernelVersion) (bool, error) {
|
||||
hostKver, err := getKernelVersion()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Pad out the kernel version lengths to match one another.
|
||||
cmpLen := gocompat.Max2(len(hostKver), len(wantKver))
|
||||
hostKver = append(hostKver, make(KernelVersion, cmpLen-len(hostKver))...)
|
||||
wantKver = append(wantKver, make(KernelVersion, cmpLen-len(wantKver))...)
|
||||
|
||||
for i := 0; i < cmpLen; i++ {
|
||||
switch gocompat.CmpCompare(hostKver[i], wantKver[i]) {
|
||||
case -1:
|
||||
// host < want
|
||||
return false, nil
|
||||
case +1:
|
||||
// host > want
|
||||
return true, nil
|
||||
case 0:
|
||||
continue
|
||||
}
|
||||
}
|
||||
// equal version values
|
||||
return true, nil
|
||||
}
|
||||
12
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/doc.go
generated
vendored
Normal file
12
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/doc.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package linux returns information about what features are supported on the
|
||||
// running kernel.
|
||||
package linux
|
||||
47
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/mount_linux.go
generated
vendored
Normal file
47
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/mount_linux.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package linux
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/kernelversion"
|
||||
)
|
||||
|
||||
// HasNewMountAPI returns whether the new fsopen(2) mount API is supported on
|
||||
// the running kernel.
|
||||
var HasNewMountAPI = gocompat.SyncOnceValue(func() bool {
|
||||
// All of the pieces of the new mount API we use (fsopen, fsconfig,
|
||||
// fsmount, open_tree) were added together in Linux 5.2[1,2], so we can
|
||||
// just check for one of the syscalls and the others should also be
|
||||
// available.
|
||||
//
|
||||
// Just try to use open_tree(2) to open a file without OPEN_TREE_CLONE.
|
||||
// This is equivalent to openat(2), but tells us if open_tree is
|
||||
// available (and thus all of the other basic new mount API syscalls).
|
||||
// open_tree(2) is most light-weight syscall to test here.
|
||||
//
|
||||
// [1]: merge commit 400913252d09
|
||||
// [2]: <https://lore.kernel.org/lkml/153754740781.17872.7869536526927736855.stgit@warthog.procyon.org.uk/>
|
||||
fd, err := unix.OpenTree(-int(unix.EBADF), "/", unix.OPEN_TREE_CLOEXEC)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_ = unix.Close(fd)
|
||||
|
||||
// RHEL 8 has a backport of fsopen(2) that appears to have some very
|
||||
// difficult to debug performance pathology. As such, it seems prudent to
|
||||
// simply reject pre-5.2 kernels.
|
||||
isNotBackport, _ := kernelversion.GreaterEqualThan(kernelversion.KernelVersion{5, 2})
|
||||
return isNotBackport
|
||||
})
|
||||
31
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/openat2_linux.go
generated
vendored
Normal file
31
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/openat2_linux.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package linux
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
|
||||
)
|
||||
|
||||
// HasOpenat2 returns whether openat2(2) is supported on the running kernel.
|
||||
var HasOpenat2 = gocompat.SyncOnceValue(func() bool {
|
||||
fd, err := unix.Openat2(unix.AT_FDCWD, ".", &unix.OpenHow{
|
||||
Flags: unix.O_PATH | unix.O_CLOEXEC,
|
||||
Resolve: unix.RESOLVE_NO_SYMLINKS | unix.RESOLVE_IN_ROOT,
|
||||
})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_ = unix.Close(fd)
|
||||
return true
|
||||
})
|
||||
544
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs/procfs_linux.go
generated
vendored
Normal file
544
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs/procfs_linux.go
generated
vendored
Normal file
@ -0,0 +1,544 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package procfs provides a safe API for operating on /proc on Linux. Note
|
||||
// that this is the *internal* procfs API, mainy needed due to Go's
|
||||
// restrictions on cyclic dependencies and its incredibly minimal visibility
|
||||
// system without making a separate internal/ package.
|
||||
package procfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/assert"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux"
|
||||
)
|
||||
|
||||
// The kernel guarantees that the root inode of a procfs mount has an
|
||||
// f_type of PROC_SUPER_MAGIC and st_ino of PROC_ROOT_INO.
|
||||
const (
|
||||
procSuperMagic = 0x9fa0 // PROC_SUPER_MAGIC
|
||||
procRootIno = 1 // PROC_ROOT_INO
|
||||
)
|
||||
|
||||
// verifyProcHandle checks that the handle is from a procfs filesystem.
|
||||
// Contrast this to [verifyProcRoot], which also verifies that the handle is
|
||||
// the root of a procfs mount.
|
||||
func verifyProcHandle(procHandle fd.Fd) error {
|
||||
if statfs, err := fd.Fstatfs(procHandle); err != nil {
|
||||
return err
|
||||
} else if statfs.Type != procSuperMagic {
|
||||
return fmt.Errorf("%w: incorrect procfs root filesystem type 0x%x", errUnsafeProcfs, statfs.Type)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyProcRoot verifies that the handle is the root of a procfs filesystem.
|
||||
// Contrast this to [verifyProcHandle], which only verifies if the handle is
|
||||
// some file on procfs (regardless of what file it is).
|
||||
func verifyProcRoot(procRoot fd.Fd) error {
|
||||
if err := verifyProcHandle(procRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
if stat, err := fd.Fstat(procRoot); err != nil {
|
||||
return err
|
||||
} else if stat.Ino != procRootIno {
|
||||
return fmt.Errorf("%w: incorrect procfs root inode number %d", errUnsafeProcfs, stat.Ino)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type procfsFeatures struct {
|
||||
// hasSubsetPid was added in Linux 5.8, along with hidepid=ptraceable (and
|
||||
// string-based hidepid= values). Before this patchset, it was not really
|
||||
// safe to try to modify procfs superblock flags because the superblock was
|
||||
// shared -- so if this feature is not available, **you should not set any
|
||||
// superblock flags**.
|
||||
//
|
||||
// 6814ef2d992a ("proc: add option to mount only a pids subset")
|
||||
// fa10fed30f25 ("proc: allow to mount many instances of proc in one pid namespace")
|
||||
// 24a71ce5c47f ("proc: instantiate only pids that we can ptrace on 'hidepid=4' mount option")
|
||||
// 1c6c4d112e81 ("proc: use human-readable values for hidepid")
|
||||
// 9ff7258575d5 ("Merge branch 'proc-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace")
|
||||
hasSubsetPid bool
|
||||
}
|
||||
|
||||
var getProcfsFeatures = gocompat.SyncOnceValue(func() procfsFeatures {
|
||||
if !linux.HasNewMountAPI() {
|
||||
return procfsFeatures{}
|
||||
}
|
||||
procfsCtx, err := fd.Fsopen("proc", unix.FSOPEN_CLOEXEC)
|
||||
if err != nil {
|
||||
return procfsFeatures{}
|
||||
}
|
||||
defer procfsCtx.Close() //nolint:errcheck // close failures aren't critical here
|
||||
|
||||
return procfsFeatures{
|
||||
hasSubsetPid: unix.FsconfigSetString(int(procfsCtx.Fd()), "subset", "pid") == nil,
|
||||
}
|
||||
})
|
||||
|
||||
func newPrivateProcMount(subset bool) (_ *Handle, Err error) {
|
||||
procfsCtx, err := fd.Fsopen("proc", unix.FSOPEN_CLOEXEC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer procfsCtx.Close() //nolint:errcheck // close failures aren't critical here
|
||||
|
||||
if subset && getProcfsFeatures().hasSubsetPid {
|
||||
// Try to configure hidepid=ptraceable,subset=pid if possible, but
|
||||
// ignore errors.
|
||||
_ = unix.FsconfigSetString(int(procfsCtx.Fd()), "hidepid", "ptraceable")
|
||||
_ = unix.FsconfigSetString(int(procfsCtx.Fd()), "subset", "pid")
|
||||
}
|
||||
|
||||
// Get an actual handle.
|
||||
if err := unix.FsconfigCreate(int(procfsCtx.Fd())); err != nil {
|
||||
return nil, os.NewSyscallError("fsconfig create procfs", err)
|
||||
}
|
||||
// TODO: Output any information from the fscontext log to debug logs.
|
||||
procRoot, err := fd.Fsmount(procfsCtx, unix.FSMOUNT_CLOEXEC, unix.MS_NODEV|unix.MS_NOEXEC|unix.MS_NOSUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
_ = procRoot.Close()
|
||||
}
|
||||
}()
|
||||
return newHandle(procRoot)
|
||||
}
|
||||
|
||||
func clonePrivateProcMount() (_ *Handle, Err error) {
|
||||
// Try to make a clone without using AT_RECURSIVE if we can. If this works,
|
||||
// we can be sure there are no over-mounts and so if the root is valid then
|
||||
// we're golden. Otherwise, we have to deal with over-mounts.
|
||||
procRoot, err := fd.OpenTree(nil, "/proc", unix.OPEN_TREE_CLONE)
|
||||
if err != nil || hookForcePrivateProcRootOpenTreeAtRecursive(procRoot) {
|
||||
procRoot, err = fd.OpenTree(nil, "/proc", unix.OPEN_TREE_CLONE|unix.AT_RECURSIVE)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating a detached procfs clone: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
_ = procRoot.Close()
|
||||
}
|
||||
}()
|
||||
return newHandle(procRoot)
|
||||
}
|
||||
|
||||
func privateProcRoot(subset bool) (*Handle, error) {
|
||||
if !linux.HasNewMountAPI() || hookForceGetProcRootUnsafe() {
|
||||
return nil, fmt.Errorf("new mount api: %w", unix.ENOTSUP)
|
||||
}
|
||||
// Try to create a new procfs mount from scratch if we can. This ensures we
|
||||
// can get a procfs mount even if /proc is fake (for whatever reason).
|
||||
procRoot, err := newPrivateProcMount(subset)
|
||||
if err != nil || hookForcePrivateProcRootOpenTree(procRoot) {
|
||||
// Try to clone /proc then...
|
||||
procRoot, err = clonePrivateProcMount()
|
||||
}
|
||||
return procRoot, err
|
||||
}
|
||||
|
||||
func unsafeHostProcRoot() (_ *Handle, Err error) {
|
||||
procRoot, err := os.OpenFile("/proc", unix.O_PATH|unix.O_NOFOLLOW|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
_ = procRoot.Close()
|
||||
}
|
||||
}()
|
||||
return newHandle(procRoot)
|
||||
}
|
||||
|
||||
// Handle is a wrapper around an *os.File handle to "/proc", which can be used
|
||||
// to do further procfs-related operations in a safe way.
|
||||
type Handle struct {
|
||||
Inner fd.Fd
|
||||
// Does this handle have subset=pid set?
|
||||
isSubset bool
|
||||
}
|
||||
|
||||
func newHandle(procRoot fd.Fd) (*Handle, error) {
|
||||
if err := verifyProcRoot(procRoot); err != nil {
|
||||
// This is only used in methods that
|
||||
_ = procRoot.Close()
|
||||
return nil, err
|
||||
}
|
||||
proc := &Handle{Inner: procRoot}
|
||||
// With subset=pid we can be sure that /proc/uptime will not exist.
|
||||
if err := fd.Faccessat(proc.Inner, "uptime", unix.F_OK, unix.AT_SYMLINK_NOFOLLOW); err != nil {
|
||||
proc.isSubset = errors.Is(err, os.ErrNotExist)
|
||||
}
|
||||
return proc, nil
|
||||
}
|
||||
|
||||
// Close closes the underlying file for the Handle.
|
||||
func (proc *Handle) Close() error { return proc.Inner.Close() }
|
||||
|
||||
var getCachedProcRoot = gocompat.SyncOnceValue(func() *Handle {
|
||||
procRoot, err := getProcRoot(true)
|
||||
if err != nil {
|
||||
return nil // just don't cache if we see an error
|
||||
}
|
||||
if !procRoot.isSubset {
|
||||
return nil // we only cache verified subset=pid handles
|
||||
}
|
||||
|
||||
// Disarm (*Handle).Close() to stop someone from accidentally closing
|
||||
// the global handle.
|
||||
procRoot.Inner = fd.NopCloser(procRoot.Inner)
|
||||
return procRoot
|
||||
})
|
||||
|
||||
// OpenProcRoot tries to open a "safer" handle to "/proc".
|
||||
func OpenProcRoot() (*Handle, error) {
|
||||
if proc := getCachedProcRoot(); proc != nil {
|
||||
return proc, nil
|
||||
}
|
||||
return getProcRoot(true)
|
||||
}
|
||||
|
||||
// OpenUnsafeProcRoot opens a handle to "/proc" without any overmounts or
|
||||
// masked paths (but also without "subset=pid").
|
||||
func OpenUnsafeProcRoot() (*Handle, error) { return getProcRoot(false) }
|
||||
|
||||
func getProcRoot(subset bool) (*Handle, error) {
|
||||
proc, err := privateProcRoot(subset)
|
||||
if err != nil {
|
||||
// Fall back to using a /proc handle if making a private mount failed.
|
||||
// If we have openat2, at least we can avoid some kinds of over-mount
|
||||
// attacks, but without openat2 there's not much we can do.
|
||||
proc, err = unsafeHostProcRoot()
|
||||
}
|
||||
return proc, err
|
||||
}
|
||||
|
||||
var hasProcThreadSelf = gocompat.SyncOnceValue(func() bool {
|
||||
return unix.Access("/proc/thread-self/", unix.F_OK) == nil
|
||||
})
|
||||
|
||||
var errUnsafeProcfs = errors.New("unsafe procfs detected")
|
||||
|
||||
// lookup is a very minimal wrapper around [procfsLookupInRoot] which is
|
||||
// intended to be called from the external API.
|
||||
func (proc *Handle) lookup(subpath string) (*os.File, error) {
|
||||
handle, err := procfsLookupInRoot(proc.Inner, subpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
// procfsBase is an enum indicating the prefix of a subpath in operations
|
||||
// involving [Handle]s.
|
||||
type procfsBase string
|
||||
|
||||
const (
|
||||
// ProcRoot refers to the root of the procfs (i.e., "/proc/<subpath>").
|
||||
ProcRoot procfsBase = "/proc"
|
||||
// ProcSelf refers to the current process' subdirectory (i.e.,
|
||||
// "/proc/self/<subpath>").
|
||||
ProcSelf procfsBase = "/proc/self"
|
||||
// ProcThreadSelf refers to the current thread's subdirectory (i.e.,
|
||||
// "/proc/thread-self/<subpath>"). In multi-threaded programs (i.e., all Go
|
||||
// programs) where one thread has a different CLONE_FS, it is possible for
|
||||
// "/proc/self" to point the wrong thread and so "/proc/thread-self" may be
|
||||
// necessary. Note that on pre-3.17 kernels, "/proc/thread-self" doesn't
|
||||
// exist and so a fallback will be used in that case.
|
||||
ProcThreadSelf procfsBase = "/proc/thread-self"
|
||||
// TODO: Switch to an interface setup so we can have a more type-safe
|
||||
// version of ProcPid and remove the need to worry about invalid string
|
||||
// values.
|
||||
)
|
||||
|
||||
// prefix returns a prefix that can be used with the given [Handle].
|
||||
func (base procfsBase) prefix(proc *Handle) (string, error) {
|
||||
switch base {
|
||||
case ProcRoot:
|
||||
return ".", nil
|
||||
case ProcSelf:
|
||||
return "self", nil
|
||||
case ProcThreadSelf:
|
||||
threadSelf := "thread-self"
|
||||
if !hasProcThreadSelf() || hookForceProcSelfTask() {
|
||||
// Pre-3.17 kernels don't have /proc/thread-self, so do it
|
||||
// manually.
|
||||
threadSelf = "self/task/" + strconv.Itoa(unix.Gettid())
|
||||
if err := fd.Faccessat(proc.Inner, threadSelf, unix.F_OK, unix.AT_SYMLINK_NOFOLLOW); err != nil || hookForceProcSelf() {
|
||||
// In this case, we running in a pid namespace that doesn't
|
||||
// match the /proc mount we have. This can happen inside runc.
|
||||
//
|
||||
// Unfortunately, there is no nice way to get the correct TID
|
||||
// to use here because of the age of the kernel, so we have to
|
||||
// just use /proc/self and hope that it works.
|
||||
threadSelf = "self"
|
||||
}
|
||||
}
|
||||
return threadSelf, nil
|
||||
}
|
||||
return "", fmt.Errorf("invalid procfs base %q", base)
|
||||
}
|
||||
|
||||
// ProcThreadSelfCloser is a callback that needs to be called when you are done
|
||||
// operating on an [os.File] fetched using [ProcThreadSelf].
|
||||
//
|
||||
// [os.File]: https://pkg.go.dev/os#File
|
||||
type ProcThreadSelfCloser func()
|
||||
|
||||
// open is the core lookup operation for [Handle]. It returns a handle to
|
||||
// "/proc/<base>/<subpath>". If the returned [ProcThreadSelfCloser] is non-nil,
|
||||
// you should call it after you are done interacting with the returned handle.
|
||||
//
|
||||
// In general you should use prefer to use the other helpers, as they remove
|
||||
// the need to interact with [procfsBase] and do not return a nil
|
||||
// [ProcThreadSelfCloser] for [procfsBase] values other than [ProcThreadSelf]
|
||||
// where it is necessary.
|
||||
func (proc *Handle) open(base procfsBase, subpath string) (_ *os.File, closer ProcThreadSelfCloser, Err error) {
|
||||
prefix, err := base.prefix(proc)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
subpath = prefix + "/" + subpath
|
||||
|
||||
switch base {
|
||||
case ProcRoot:
|
||||
file, err := proc.lookup(subpath)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// The Handle handle in use might be a subset=pid one, which will
|
||||
// result in spurious errors. In this case, just open a temporary
|
||||
// unmasked procfs handle for this operation.
|
||||
proc, err2 := OpenUnsafeProcRoot() // !subset=pid
|
||||
if err2 != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer proc.Close() //nolint:errcheck // close failures aren't critical here
|
||||
|
||||
file, err = proc.lookup(subpath)
|
||||
}
|
||||
return file, nil, err
|
||||
|
||||
case ProcSelf:
|
||||
file, err := proc.lookup(subpath)
|
||||
return file, nil, err
|
||||
|
||||
case ProcThreadSelf:
|
||||
// We need to lock our thread until the caller is done with the handle
|
||||
// because between getting the handle and using it we could get
|
||||
// interrupted by the Go runtime and hit the case where the underlying
|
||||
// thread is swapped out and the original thread is killed, resulting
|
||||
// in pull-your-hair-out-hard-to-debug issues in the caller.
|
||||
runtime.LockOSThread()
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
runtime.UnlockOSThread()
|
||||
closer = nil
|
||||
}
|
||||
}()
|
||||
|
||||
file, err := proc.lookup(subpath)
|
||||
return file, runtime.UnlockOSThread, err
|
||||
}
|
||||
// should never be reached
|
||||
return nil, nil, fmt.Errorf("[internal error] invalid procfs base %q", base)
|
||||
}
|
||||
|
||||
// OpenThreadSelf returns a handle to "/proc/thread-self/<subpath>" (or an
|
||||
// equivalent handle on older kernels where "/proc/thread-self" doesn't exist).
|
||||
// Once finished with the handle, you must call the returned closer function
|
||||
// (runtime.UnlockOSThread). You must not pass the returned *os.File to other
|
||||
// Go threads or use the handle after calling the closer.
|
||||
func (proc *Handle) OpenThreadSelf(subpath string) (_ *os.File, _ ProcThreadSelfCloser, Err error) {
|
||||
return proc.open(ProcThreadSelf, subpath)
|
||||
}
|
||||
|
||||
// OpenSelf returns a handle to /proc/self/<subpath>.
|
||||
func (proc *Handle) OpenSelf(subpath string) (*os.File, error) {
|
||||
file, closer, err := proc.open(ProcSelf, subpath)
|
||||
assert.Assert(closer == nil, "closer for ProcSelf must be nil")
|
||||
return file, err
|
||||
}
|
||||
|
||||
// OpenRoot returns a handle to /proc/<subpath>.
|
||||
func (proc *Handle) OpenRoot(subpath string) (*os.File, error) {
|
||||
file, closer, err := proc.open(ProcRoot, subpath)
|
||||
assert.Assert(closer == nil, "closer for ProcRoot must be nil")
|
||||
return file, err
|
||||
}
|
||||
|
||||
// OpenPid returns a handle to /proc/$pid/<subpath> (pid can be a pid or tid).
|
||||
// This is mainly intended for usage when operating on other processes.
|
||||
func (proc *Handle) OpenPid(pid int, subpath string) (*os.File, error) {
|
||||
return proc.OpenRoot(strconv.Itoa(pid) + "/" + subpath)
|
||||
}
|
||||
|
||||
// checkSubpathOvermount checks if the dirfd and path combination is on the
|
||||
// same mount as the given root.
|
||||
func checkSubpathOvermount(root, dir fd.Fd, path string) error {
|
||||
// Get the mntID of our procfs handle.
|
||||
expectedMountID, err := fd.GetMountID(root, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("get root mount id: %w", err)
|
||||
}
|
||||
// Get the mntID of the target magic-link.
|
||||
gotMountID, err := fd.GetMountID(dir, path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get subpath mount id: %w", err)
|
||||
}
|
||||
// As long as the directory mount is alive, even with wrapping mount IDs,
|
||||
// we would expect to see a different mount ID here. (Of course, if we're
|
||||
// using unsafeHostProcRoot() then an attaker could change this after we
|
||||
// did this check.)
|
||||
if expectedMountID != gotMountID {
|
||||
return fmt.Errorf("%w: subpath %s/%s has an overmount obscuring the real path (mount ids do not match %d != %d)",
|
||||
errUnsafeProcfs, dir.Name(), path, expectedMountID, gotMountID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Readlink performs a readlink operation on "/proc/<base>/<subpath>" in a way
|
||||
// that should be free from race attacks. This is most commonly used to get the
|
||||
// real path of a file by looking at "/proc/self/fd/$n", with the same safety
|
||||
// protections as [Open] (as well as some additional checks against
|
||||
// overmounts).
|
||||
func (proc *Handle) Readlink(base procfsBase, subpath string) (string, error) {
|
||||
link, closer, err := proc.open(base, subpath)
|
||||
if closer != nil {
|
||||
defer closer()
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get safe %s/%s handle: %w", base, subpath, err)
|
||||
}
|
||||
defer link.Close() //nolint:errcheck // close failures aren't critical here
|
||||
|
||||
// Try to detect if there is a mount on top of the magic-link. This should
|
||||
// be safe in general (a mount on top of the path afterwards would not
|
||||
// affect the handle itself) and will definitely be safe if we are using
|
||||
// privateProcRoot() (at least since Linux 5.12[1], when anonymous mount
|
||||
// namespaces were completely isolated from external mounts including mount
|
||||
// propagation events).
|
||||
//
|
||||
// [1]: Linux commit ee2e3f50629f ("mount: fix mounting of detached mounts
|
||||
// onto targets that reside on shared mounts").
|
||||
if err := checkSubpathOvermount(proc.Inner, link, ""); err != nil {
|
||||
return "", fmt.Errorf("check safety of %s/%s magiclink: %w", base, subpath, err)
|
||||
}
|
||||
|
||||
// readlinkat implies AT_EMPTY_PATH since Linux 2.6.39. See Linux commit
|
||||
// 65cfc6722361 ("readlinkat(), fchownat() and fstatat() with empty
|
||||
// relative pathnames").
|
||||
return fd.Readlinkat(link, "")
|
||||
}
|
||||
|
||||
// ProcSelfFdReadlink gets the real path of the given file by looking at
|
||||
// readlink(/proc/thread-self/fd/$n).
|
||||
//
|
||||
// This is just a wrapper around [Handle.Readlink].
|
||||
func ProcSelfFdReadlink(fd fd.Fd) (string, error) {
|
||||
procRoot, err := OpenProcRoot() // subset=pid
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer procRoot.Close() //nolint:errcheck // close failures aren't critical here
|
||||
|
||||
fdPath := "fd/" + strconv.Itoa(int(fd.Fd()))
|
||||
return procRoot.Readlink(ProcThreadSelf, fdPath)
|
||||
}
|
||||
|
||||
// CheckProcSelfFdPath returns whether the given file handle matches the
|
||||
// expected path. (This is inherently racy.)
|
||||
func CheckProcSelfFdPath(path string, file fd.Fd) error {
|
||||
if err := fd.IsDeadInode(file); err != nil {
|
||||
return err
|
||||
}
|
||||
actualPath, err := ProcSelfFdReadlink(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get path of handle: %w", err)
|
||||
}
|
||||
if actualPath != path {
|
||||
return fmt.Errorf("%w: handle path %q doesn't match expected path %q", internal.ErrPossibleBreakout, actualPath, path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReopenFd takes an existing file descriptor and "re-opens" it through
|
||||
// /proc/thread-self/fd/<fd>. This allows for O_PATH file descriptors to be
|
||||
// upgraded to regular file descriptors, as well as changing the open mode of a
|
||||
// regular file descriptor. Some filesystems have unique handling of open(2)
|
||||
// which make this incredibly useful (such as /dev/ptmx).
|
||||
func ReopenFd(handle fd.Fd, flags int) (*os.File, error) {
|
||||
procRoot, err := OpenProcRoot() // subset=pid
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer procRoot.Close() //nolint:errcheck // close failures aren't critical here
|
||||
|
||||
// We can't operate on /proc/thread-self/fd/$n directly when doing a
|
||||
// re-open, so we need to open /proc/thread-self/fd and then open a single
|
||||
// final component.
|
||||
procFdDir, closer, err := procRoot.OpenThreadSelf("fd/")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get safe /proc/thread-self/fd handle: %w", err)
|
||||
}
|
||||
defer procFdDir.Close() //nolint:errcheck // close failures aren't critical here
|
||||
defer closer()
|
||||
|
||||
// Try to detect if there is a mount on top of the magic-link we are about
|
||||
// to open. If we are using unsafeHostProcRoot(), this could change after
|
||||
// we check it (and there's nothing we can do about that) but for
|
||||
// privateProcRoot() this should be guaranteed to be safe (at least since
|
||||
// Linux 5.12[1], when anonymous mount namespaces were completely isolated
|
||||
// from external mounts including mount propagation events).
|
||||
//
|
||||
// [1]: Linux commit ee2e3f50629f ("mount: fix mounting of detached mounts
|
||||
// onto targets that reside on shared mounts").
|
||||
fdStr := strconv.Itoa(int(handle.Fd()))
|
||||
if err := checkSubpathOvermount(procRoot.Inner, procFdDir, fdStr); err != nil {
|
||||
return nil, fmt.Errorf("check safety of /proc/thread-self/fd/%s magiclink: %w", fdStr, err)
|
||||
}
|
||||
|
||||
flags |= unix.O_CLOEXEC
|
||||
// Rather than just wrapping fd.Openat, open-code it so we can copy
|
||||
// handle.Name().
|
||||
reopenFd, err := unix.Openat(int(procFdDir.Fd()), fdStr, flags, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reopen fd %d: %w", handle.Fd(), err)
|
||||
}
|
||||
return os.NewFile(uintptr(reopenFd), handle.Name()), nil
|
||||
}
|
||||
|
||||
// Test hooks used in the procfs tests to verify that the fallback logic works.
|
||||
// See testing_mocks_linux_test.go and procfs_linux_test.go for more details.
|
||||
var (
|
||||
hookForcePrivateProcRootOpenTree = hookDummyFile
|
||||
hookForcePrivateProcRootOpenTreeAtRecursive = hookDummyFile
|
||||
hookForceGetProcRootUnsafe = hookDummy
|
||||
|
||||
hookForceProcSelfTask = hookDummy
|
||||
hookForceProcSelf = hookDummy
|
||||
)
|
||||
|
||||
func hookDummy() bool { return false }
|
||||
func hookDummyFile(_ io.Closer) bool { return false }
|
||||
222
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs/procfs_lookup_linux.go
generated
vendored
Normal file
222
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs/procfs_lookup_linux.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// This code is adapted to be a minimal version of the libpathrs proc resolver
|
||||
// <https://github.com/opensuse/libpathrs/blob/v0.1.3/src/resolvers/procfs.rs>.
|
||||
// As we only need O_PATH|O_NOFOLLOW support, this is not too much to port.
|
||||
|
||||
package procfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/internal/consts"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux"
|
||||
)
|
||||
|
||||
// procfsLookupInRoot is a stripped down version of completeLookupInRoot,
|
||||
// entirely designed to support the very small set of features necessary to
|
||||
// make procfs handling work. Unlike completeLookupInRoot, we always have
|
||||
// O_PATH|O_NOFOLLOW behaviour for trailing symlinks.
|
||||
//
|
||||
// The main restrictions are:
|
||||
//
|
||||
// - ".." is not supported (as it requires either os.Root-style replays,
|
||||
// which is more bug-prone; or procfs verification, which is not possible
|
||||
// due to re-entrancy issues).
|
||||
// - Absolute symlinks for the same reason (and all absolute symlinks in
|
||||
// procfs are magic-links, which we want to skip anyway).
|
||||
// - If statx is supported (checkSymlinkOvermount), any mount-point crossings
|
||||
// (which is the main attack of concern against /proc).
|
||||
// - Partial lookups are not supported, so the symlink stack is not needed.
|
||||
// - Trailing slash special handling is not necessary in most cases (if we
|
||||
// operating on procfs, it's usually with programmer-controlled strings
|
||||
// that will then be re-opened), so we skip it since whatever re-opens it
|
||||
// can deal with it. It's a creature comfort anyway.
|
||||
//
|
||||
// If the system supports openat2(), this is implemented using equivalent flags
|
||||
// (RESOLVE_BENEATH | RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS).
|
||||
func procfsLookupInRoot(procRoot fd.Fd, unsafePath string) (Handle *os.File, _ error) {
|
||||
unsafePath = filepath.ToSlash(unsafePath) // noop
|
||||
|
||||
// Make sure that an empty unsafe path still returns something sane, even
|
||||
// with openat2 (which doesn't have AT_EMPTY_PATH semantics yet).
|
||||
if unsafePath == "" {
|
||||
unsafePath = "."
|
||||
}
|
||||
|
||||
// This is already checked by getProcRoot, but make sure here since the
|
||||
// core security of this lookup is based on this assumption.
|
||||
if err := verifyProcRoot(procRoot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if linux.HasOpenat2() {
|
||||
// We prefer being able to use RESOLVE_NO_XDEV if we can, to be
|
||||
// absolutely sure we are operating on a clean /proc handle that
|
||||
// doesn't have any cheeky overmounts that could trick us (including
|
||||
// symlink mounts on top of /proc/thread-self). RESOLVE_BENEATH isn't
|
||||
// strictly needed, but just use it since we have it.
|
||||
//
|
||||
// NOTE: /proc/self is technically a magic-link (the contents of the
|
||||
// symlink are generated dynamically), but it doesn't use
|
||||
// nd_jump_link() so RESOLVE_NO_MAGICLINKS allows it.
|
||||
//
|
||||
// TODO: It would be nice to have RESOLVE_NO_DOTDOT, purely for
|
||||
// self-consistency with the backup O_PATH resolver.
|
||||
handle, err := fd.Openat2(procRoot, unsafePath, &unix.OpenHow{
|
||||
Flags: unix.O_PATH | unix.O_NOFOLLOW | unix.O_CLOEXEC,
|
||||
Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_XDEV | unix.RESOLVE_NO_MAGICLINKS,
|
||||
})
|
||||
if err != nil {
|
||||
// TODO: Once we bump the minimum Go version to 1.20, we can use
|
||||
// multiple %w verbs for this wrapping. For now we need to use a
|
||||
// compatibility shim for older Go versions.
|
||||
// err = fmt.Errorf("%w: %w", errUnsafeProcfs, err)
|
||||
return nil, gocompat.WrapBaseError(err, errUnsafeProcfs)
|
||||
}
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
// To mirror openat2(RESOLVE_BENEATH), we need to return an error if the
|
||||
// path is absolute.
|
||||
if path.IsAbs(unsafePath) {
|
||||
return nil, fmt.Errorf("%w: cannot resolve absolute paths in procfs resolver", internal.ErrPossibleBreakout)
|
||||
}
|
||||
|
||||
currentDir, err := fd.Dup(procRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("clone root fd: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
// If a handle is not returned, close the internal handle.
|
||||
if Handle == nil {
|
||||
_ = currentDir.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
linksWalked int
|
||||
currentPath string
|
||||
remainingPath = unsafePath
|
||||
)
|
||||
for remainingPath != "" {
|
||||
// Get the next path component.
|
||||
var part string
|
||||
if i := strings.IndexByte(remainingPath, '/'); i == -1 {
|
||||
part, remainingPath = remainingPath, ""
|
||||
} else {
|
||||
part, remainingPath = remainingPath[:i], remainingPath[i+1:]
|
||||
}
|
||||
if part == "" {
|
||||
// no-op component, but treat it the same as "."
|
||||
part = "."
|
||||
}
|
||||
if part == ".." {
|
||||
// not permitted
|
||||
return nil, fmt.Errorf("%w: cannot walk into '..' in procfs resolver", internal.ErrPossibleBreakout)
|
||||
}
|
||||
|
||||
// Apply the component lexically to the path we are building.
|
||||
// currentPath does not contain any symlinks, and we are lexically
|
||||
// dealing with a single component, so it's okay to do a filepath.Clean
|
||||
// here. (Not to mention that ".." isn't allowed.)
|
||||
nextPath := path.Join("/", currentPath, part)
|
||||
// If we logically hit the root, just clone the root rather than
|
||||
// opening the part and doing all of the other checks.
|
||||
if nextPath == "/" {
|
||||
// Jump to root.
|
||||
rootClone, err := fd.Dup(procRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("clone root fd: %w", err)
|
||||
}
|
||||
_ = currentDir.Close()
|
||||
currentDir = rootClone
|
||||
currentPath = nextPath
|
||||
continue
|
||||
}
|
||||
|
||||
// Try to open the next component.
|
||||
nextDir, err := fd.Openat(currentDir, part, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure we are still on procfs and haven't crossed mounts.
|
||||
if err := verifyProcHandle(nextDir); err != nil {
|
||||
_ = nextDir.Close()
|
||||
return nil, fmt.Errorf("check %q component is on procfs: %w", part, err)
|
||||
}
|
||||
if err := checkSubpathOvermount(procRoot, nextDir, ""); err != nil {
|
||||
_ = nextDir.Close()
|
||||
return nil, fmt.Errorf("check %q component is not overmounted: %w", part, err)
|
||||
}
|
||||
|
||||
// We are emulating O_PATH|O_NOFOLLOW, so we only need to traverse into
|
||||
// trailing symlinks if we are not the final component. Otherwise we
|
||||
// can just return the currentDir.
|
||||
if remainingPath != "" {
|
||||
st, err := nextDir.Stat()
|
||||
if err != nil {
|
||||
_ = nextDir.Close()
|
||||
return nil, fmt.Errorf("stat component %q: %w", part, err)
|
||||
}
|
||||
|
||||
if st.Mode()&os.ModeType == os.ModeSymlink {
|
||||
// readlinkat implies AT_EMPTY_PATH since Linux 2.6.39. See
|
||||
// Linux commit 65cfc6722361 ("readlinkat(), fchownat() and
|
||||
// fstatat() with empty relative pathnames").
|
||||
linkDest, err := fd.Readlinkat(nextDir, "")
|
||||
// We don't need the handle anymore.
|
||||
_ = nextDir.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
linksWalked++
|
||||
if linksWalked > consts.MaxSymlinkLimit {
|
||||
return nil, &os.PathError{Op: "securejoin.procfsLookupInRoot", Path: "/proc/" + unsafePath, Err: unix.ELOOP}
|
||||
}
|
||||
|
||||
// Update our logical remaining path.
|
||||
remainingPath = linkDest + "/" + remainingPath
|
||||
// Absolute symlinks are probably magiclinks, we reject them.
|
||||
if path.IsAbs(linkDest) {
|
||||
return nil, fmt.Errorf("%w: cannot jump to / in procfs resolver -- possible magiclink", internal.ErrPossibleBreakout)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Walk into the next component.
|
||||
_ = currentDir.Close()
|
||||
currentDir = nextDir
|
||||
currentPath = nextPath
|
||||
}
|
||||
|
||||
// One final sanity-check.
|
||||
if err := verifyProcHandle(currentDir); err != nil {
|
||||
return nil, fmt.Errorf("check final handle is on procfs: %w", err)
|
||||
}
|
||||
if err := checkSubpathOvermount(procRoot, currentDir, ""); err != nil {
|
||||
return nil, fmt.Errorf("check final handle is not overmounted: %w", err)
|
||||
}
|
||||
return currentDir, nil
|
||||
}
|
||||
399
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/lookup_linux.go
generated
vendored
Normal file
399
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/lookup_linux.go
generated
vendored
Normal file
@ -0,0 +1,399 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package pathrs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/internal/consts"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs"
|
||||
)
|
||||
|
||||
type symlinkStackEntry struct {
|
||||
// (dir, remainingPath) is what we would've returned if the link didn't
|
||||
// exist. This matches what openat2(RESOLVE_IN_ROOT) would return in
|
||||
// this case.
|
||||
dir *os.File
|
||||
remainingPath string
|
||||
// linkUnwalked is the remaining path components from the original
|
||||
// Readlink which we have yet to walk. When this slice is empty, we
|
||||
// drop the link from the stack.
|
||||
linkUnwalked []string
|
||||
}
|
||||
|
||||
func (se symlinkStackEntry) String() string {
|
||||
return fmt.Sprintf("<%s>/%s [->%s]", se.dir.Name(), se.remainingPath, strings.Join(se.linkUnwalked, "/"))
|
||||
}
|
||||
|
||||
func (se symlinkStackEntry) Close() {
|
||||
_ = se.dir.Close()
|
||||
}
|
||||
|
||||
type symlinkStack []*symlinkStackEntry
|
||||
|
||||
func (s *symlinkStack) IsEmpty() bool {
|
||||
return s == nil || len(*s) == 0
|
||||
}
|
||||
|
||||
func (s *symlinkStack) Close() {
|
||||
if s != nil {
|
||||
for _, link := range *s {
|
||||
link.Close()
|
||||
}
|
||||
// TODO: Switch to clear once we switch to Go 1.21.
|
||||
*s = nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
errEmptyStack = errors.New("[internal] stack is empty")
|
||||
errBrokenSymlinkStack = errors.New("[internal error] broken symlink stack")
|
||||
)
|
||||
|
||||
func (s *symlinkStack) popPart(part string) error {
|
||||
if s == nil || s.IsEmpty() {
|
||||
// If there is nothing in the symlink stack, then the part was from the
|
||||
// real path provided by the user, and this is a no-op.
|
||||
return errEmptyStack
|
||||
}
|
||||
if part == "." {
|
||||
// "." components are no-ops -- we drop them when doing SwapLink.
|
||||
return nil
|
||||
}
|
||||
|
||||
tailEntry := (*s)[len(*s)-1]
|
||||
|
||||
// Double-check that we are popping the component we expect.
|
||||
if len(tailEntry.linkUnwalked) == 0 {
|
||||
return fmt.Errorf("%w: trying to pop component %q of empty stack entry %s", errBrokenSymlinkStack, part, tailEntry)
|
||||
}
|
||||
headPart := tailEntry.linkUnwalked[0]
|
||||
if headPart != part {
|
||||
return fmt.Errorf("%w: trying to pop component %q but the last stack entry is %s (%q)", errBrokenSymlinkStack, part, tailEntry, headPart)
|
||||
}
|
||||
|
||||
// Drop the component, but keep the entry around in case we are dealing
|
||||
// with a "tail-chained" symlink.
|
||||
tailEntry.linkUnwalked = tailEntry.linkUnwalked[1:]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *symlinkStack) PopPart(part string) error {
|
||||
if err := s.popPart(part); err != nil {
|
||||
if errors.Is(err, errEmptyStack) {
|
||||
// Skip empty stacks.
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Clean up any of the trailing stack entries that are empty.
|
||||
for lastGood := len(*s) - 1; lastGood >= 0; lastGood-- {
|
||||
entry := (*s)[lastGood]
|
||||
if len(entry.linkUnwalked) > 0 {
|
||||
break
|
||||
}
|
||||
entry.Close()
|
||||
(*s) = (*s)[:lastGood]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *symlinkStack) push(dir *os.File, remainingPath, linkTarget string) error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
// Split the link target and clean up any "" parts.
|
||||
linkTargetParts := gocompat.SlicesDeleteFunc(
|
||||
strings.Split(linkTarget, "/"),
|
||||
func(part string) bool { return part == "" || part == "." })
|
||||
|
||||
// Copy the directory so the caller doesn't close our copy.
|
||||
dirCopy, err := fd.Dup(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add to the stack.
|
||||
*s = append(*s, &symlinkStackEntry{
|
||||
dir: dirCopy,
|
||||
remainingPath: remainingPath,
|
||||
linkUnwalked: linkTargetParts,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *symlinkStack) SwapLink(linkPart string, dir *os.File, remainingPath, linkTarget string) error {
|
||||
// If we are currently inside a symlink resolution, remove the symlink
|
||||
// component from the last symlink entry, but don't remove the entry even
|
||||
// if it's empty. If we are a "tail-chained" symlink (a trailing symlink we
|
||||
// hit during a symlink resolution) we need to keep the old symlink until
|
||||
// we finish the resolution.
|
||||
if err := s.popPart(linkPart); err != nil {
|
||||
if !errors.Is(err, errEmptyStack) {
|
||||
return err
|
||||
}
|
||||
// Push the component regardless of whether the stack was empty.
|
||||
}
|
||||
return s.push(dir, remainingPath, linkTarget)
|
||||
}
|
||||
|
||||
func (s *symlinkStack) PopTopSymlink() (*os.File, string, bool) {
|
||||
if s == nil || s.IsEmpty() {
|
||||
return nil, "", false
|
||||
}
|
||||
tailEntry := (*s)[0]
|
||||
*s = (*s)[1:]
|
||||
return tailEntry.dir, tailEntry.remainingPath, true
|
||||
}
|
||||
|
||||
// partialLookupInRoot tries to lookup as much of the request path as possible
|
||||
// within the provided root (a-la RESOLVE_IN_ROOT) and opens the final existing
|
||||
// component of the requested path, returning a file handle to the final
|
||||
// existing component and a string containing the remaining path components.
|
||||
func partialLookupInRoot(root fd.Fd, unsafePath string) (*os.File, string, error) {
|
||||
return lookupInRoot(root, unsafePath, true)
|
||||
}
|
||||
|
||||
func completeLookupInRoot(root fd.Fd, unsafePath string) (*os.File, error) {
|
||||
handle, remainingPath, err := lookupInRoot(root, unsafePath, false)
|
||||
if remainingPath != "" && err == nil {
|
||||
// should never happen
|
||||
err = fmt.Errorf("[bug] non-empty remaining path when doing a non-partial lookup: %q", remainingPath)
|
||||
}
|
||||
// lookupInRoot(partial=false) will always close the handle if an error is
|
||||
// returned, so no need to double-check here.
|
||||
return handle, err
|
||||
}
|
||||
|
||||
func lookupInRoot(root fd.Fd, unsafePath string, partial bool) (Handle *os.File, _ string, _ error) {
|
||||
unsafePath = filepath.ToSlash(unsafePath) // noop
|
||||
|
||||
// This is very similar to SecureJoin, except that we operate on the
|
||||
// components using file descriptors. We then return the last component we
|
||||
// managed open, along with the remaining path components not opened.
|
||||
|
||||
// Try to use openat2 if possible.
|
||||
if linux.HasOpenat2() {
|
||||
return lookupOpenat2(root, unsafePath, partial)
|
||||
}
|
||||
|
||||
// Get the "actual" root path from /proc/self/fd. This is necessary if the
|
||||
// root is some magic-link like /proc/$pid/root, in which case we want to
|
||||
// make sure when we do procfs.CheckProcSelfFdPath that we are using the
|
||||
// correct root path.
|
||||
logicalRootPath, err := procfs.ProcSelfFdReadlink(root)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("get real root path: %w", err)
|
||||
}
|
||||
|
||||
currentDir, err := fd.Dup(root)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("clone root fd: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
// If a handle is not returned, close the internal handle.
|
||||
if Handle == nil {
|
||||
_ = currentDir.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// symlinkStack is used to emulate how openat2(RESOLVE_IN_ROOT) treats
|
||||
// dangling symlinks. If we hit a non-existent path while resolving a
|
||||
// symlink, we need to return the (dir, remainingPath) that we had when we
|
||||
// hit the symlink (treating the symlink as though it were a regular file).
|
||||
// The set of (dir, remainingPath) sets is stored within the symlinkStack
|
||||
// and we add and remove parts when we hit symlink and non-symlink
|
||||
// components respectively. We need a stack because of recursive symlinks
|
||||
// (symlinks that contain symlink components in their target).
|
||||
//
|
||||
// Note that the stack is ONLY used for book-keeping. All of the actual
|
||||
// path walking logic is still based on currentPath/remainingPath and
|
||||
// currentDir (as in SecureJoin).
|
||||
var symStack *symlinkStack
|
||||
if partial {
|
||||
symStack = new(symlinkStack)
|
||||
defer symStack.Close()
|
||||
}
|
||||
|
||||
var (
|
||||
linksWalked int
|
||||
currentPath string
|
||||
remainingPath = unsafePath
|
||||
)
|
||||
for remainingPath != "" {
|
||||
// Save the current remaining path so if the part is not real we can
|
||||
// return the path including the component.
|
||||
oldRemainingPath := remainingPath
|
||||
|
||||
// Get the next path component.
|
||||
var part string
|
||||
if i := strings.IndexByte(remainingPath, '/'); i == -1 {
|
||||
part, remainingPath = remainingPath, ""
|
||||
} else {
|
||||
part, remainingPath = remainingPath[:i], remainingPath[i+1:]
|
||||
}
|
||||
// If we hit an empty component, we need to treat it as though it is
|
||||
// "." so that trailing "/" and "//" components on a non-directory
|
||||
// correctly return the right error code.
|
||||
if part == "" {
|
||||
part = "."
|
||||
}
|
||||
|
||||
// Apply the component lexically to the path we are building.
|
||||
// currentPath does not contain any symlinks, and we are lexically
|
||||
// dealing with a single component, so it's okay to do a filepath.Clean
|
||||
// here.
|
||||
nextPath := path.Join("/", currentPath, part)
|
||||
// If we logically hit the root, just clone the root rather than
|
||||
// opening the part and doing all of the other checks.
|
||||
if nextPath == "/" {
|
||||
if err := symStack.PopPart(part); err != nil {
|
||||
return nil, "", fmt.Errorf("walking into root with part %q failed: %w", part, err)
|
||||
}
|
||||
// Jump to root.
|
||||
rootClone, err := fd.Dup(root)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("clone root fd: %w", err)
|
||||
}
|
||||
_ = currentDir.Close()
|
||||
currentDir = rootClone
|
||||
currentPath = nextPath
|
||||
continue
|
||||
}
|
||||
|
||||
// Try to open the next component.
|
||||
nextDir, err := fd.Openat(currentDir, part, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
|
||||
switch err {
|
||||
case nil:
|
||||
st, err := nextDir.Stat()
|
||||
if err != nil {
|
||||
_ = nextDir.Close()
|
||||
return nil, "", fmt.Errorf("stat component %q: %w", part, err)
|
||||
}
|
||||
|
||||
switch st.Mode() & os.ModeType { //nolint:exhaustive // just a glorified if statement
|
||||
case os.ModeSymlink:
|
||||
// readlinkat implies AT_EMPTY_PATH since Linux 2.6.39. See
|
||||
// Linux commit 65cfc6722361 ("readlinkat(), fchownat() and
|
||||
// fstatat() with empty relative pathnames").
|
||||
linkDest, err := fd.Readlinkat(nextDir, "")
|
||||
// We don't need the handle anymore.
|
||||
_ = nextDir.Close()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
linksWalked++
|
||||
if linksWalked > consts.MaxSymlinkLimit {
|
||||
return nil, "", &os.PathError{Op: "securejoin.lookupInRoot", Path: logicalRootPath + "/" + unsafePath, Err: unix.ELOOP}
|
||||
}
|
||||
|
||||
// Swap out the symlink's component for the link entry itself.
|
||||
if err := symStack.SwapLink(part, currentDir, oldRemainingPath, linkDest); err != nil {
|
||||
return nil, "", fmt.Errorf("walking into symlink %q failed: push symlink: %w", part, err)
|
||||
}
|
||||
|
||||
// Update our logical remaining path.
|
||||
remainingPath = linkDest + "/" + remainingPath
|
||||
// Absolute symlinks reset any work we've already done.
|
||||
if path.IsAbs(linkDest) {
|
||||
// Jump to root.
|
||||
rootClone, err := fd.Dup(root)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("clone root fd: %w", err)
|
||||
}
|
||||
_ = currentDir.Close()
|
||||
currentDir = rootClone
|
||||
currentPath = "/"
|
||||
}
|
||||
|
||||
default:
|
||||
// If we are dealing with a directory, simply walk into it.
|
||||
_ = currentDir.Close()
|
||||
currentDir = nextDir
|
||||
currentPath = nextPath
|
||||
|
||||
// The part was real, so drop it from the symlink stack.
|
||||
if err := symStack.PopPart(part); err != nil {
|
||||
return nil, "", fmt.Errorf("walking into directory %q failed: %w", part, err)
|
||||
}
|
||||
|
||||
// If we are operating on a .., make sure we haven't escaped.
|
||||
// We only have to check for ".." here because walking down
|
||||
// into a regular component component cannot cause you to
|
||||
// escape. This mirrors the logic in RESOLVE_IN_ROOT, except we
|
||||
// have to check every ".." rather than only checking after a
|
||||
// rename or mount on the system.
|
||||
if part == ".." {
|
||||
// Make sure the root hasn't moved.
|
||||
if err := procfs.CheckProcSelfFdPath(logicalRootPath, root); err != nil {
|
||||
return nil, "", fmt.Errorf("root path moved during lookup: %w", err)
|
||||
}
|
||||
// Make sure the path is what we expect.
|
||||
fullPath := logicalRootPath + nextPath
|
||||
if err := procfs.CheckProcSelfFdPath(fullPath, currentDir); err != nil {
|
||||
return nil, "", fmt.Errorf("walking into %q had unexpected result: %w", part, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if !partial {
|
||||
return nil, "", err
|
||||
}
|
||||
// If there are any remaining components in the symlink stack, we
|
||||
// are still within a symlink resolution and thus we hit a dangling
|
||||
// symlink. So pretend that the first symlink in the stack we hit
|
||||
// was an ENOENT (to match openat2).
|
||||
if oldDir, remainingPath, ok := symStack.PopTopSymlink(); ok {
|
||||
_ = currentDir.Close()
|
||||
return oldDir, remainingPath, err
|
||||
}
|
||||
// We have hit a final component that doesn't exist, so we have our
|
||||
// partial open result. Note that we have to use the OLD remaining
|
||||
// path, since the lookup failed.
|
||||
return currentDir, oldRemainingPath, err
|
||||
}
|
||||
}
|
||||
|
||||
// If the unsafePath had a trailing slash, we need to make sure we try to
|
||||
// do a relative "." open so that we will correctly return an error when
|
||||
// the final component is a non-directory (to match openat2). In the
|
||||
// context of openat2, a trailing slash and a trailing "/." are completely
|
||||
// equivalent.
|
||||
if strings.HasSuffix(unsafePath, "/") {
|
||||
nextDir, err := fd.Openat(currentDir, ".", unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
if !partial {
|
||||
_ = currentDir.Close()
|
||||
currentDir = nil
|
||||
}
|
||||
return currentDir, "", err
|
||||
}
|
||||
_ = currentDir.Close()
|
||||
currentDir = nextDir
|
||||
}
|
||||
|
||||
// All of the components existed!
|
||||
return currentDir, "", nil
|
||||
}
|
||||
246
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/mkdir_linux.go
generated
vendored
Normal file
246
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/mkdir_linux.go
generated
vendored
Normal file
@ -0,0 +1,246 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package pathrs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux"
|
||||
)
|
||||
|
||||
var errInvalidMode = errors.New("invalid permission mode")
|
||||
|
||||
// modePermExt is like os.ModePerm except that it also includes the set[ug]id
|
||||
// and sticky bits.
|
||||
const modePermExt = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky
|
||||
|
||||
//nolint:cyclop // this function needs to handle a lot of cases
|
||||
func toUnixMode(mode os.FileMode) (uint32, error) {
|
||||
sysMode := uint32(mode.Perm())
|
||||
if mode&os.ModeSetuid != 0 {
|
||||
sysMode |= unix.S_ISUID
|
||||
}
|
||||
if mode&os.ModeSetgid != 0 {
|
||||
sysMode |= unix.S_ISGID
|
||||
}
|
||||
if mode&os.ModeSticky != 0 {
|
||||
sysMode |= unix.S_ISVTX
|
||||
}
|
||||
// We don't allow file type bits.
|
||||
if mode&os.ModeType != 0 {
|
||||
return 0, fmt.Errorf("%w %+.3o (%s): type bits not permitted", errInvalidMode, mode, mode)
|
||||
}
|
||||
// We don't allow other unknown modes.
|
||||
if mode&^modePermExt != 0 || sysMode&unix.S_IFMT != 0 {
|
||||
return 0, fmt.Errorf("%w %+.3o (%s): unknown mode bits", errInvalidMode, mode, mode)
|
||||
}
|
||||
return sysMode, nil
|
||||
}
|
||||
|
||||
// MkdirAllHandle is equivalent to [MkdirAll], except that it is safer to use
|
||||
// in two respects:
|
||||
//
|
||||
// - The caller provides the root directory as an *[os.File] (preferably O_PATH)
|
||||
// handle. This means that the caller can be sure which root directory is
|
||||
// being used. Note that this can be emulated by using /proc/self/fd/... as
|
||||
// the root path with [os.MkdirAll].
|
||||
//
|
||||
// - Once all of the directories have been created, an *[os.File] O_PATH handle
|
||||
// to the directory at unsafePath is returned to the caller. This is done in
|
||||
// an effectively-race-free way (an attacker would only be able to swap the
|
||||
// final directory component), which is not possible to emulate with
|
||||
// [MkdirAll].
|
||||
//
|
||||
// In addition, the returned handle is obtained far more efficiently than doing
|
||||
// a brand new lookup of unsafePath (such as with [SecureJoin] or openat2) after
|
||||
// doing [MkdirAll]. If you intend to open the directory after creating it, you
|
||||
// should use MkdirAllHandle.
|
||||
//
|
||||
// [SecureJoin]: https://pkg.go.dev/github.com/cyphar/filepath-securejoin#SecureJoin
|
||||
func MkdirAllHandle(root *os.File, unsafePath string, mode os.FileMode) (_ *os.File, Err error) {
|
||||
unixMode, err := toUnixMode(mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// On Linux, mkdirat(2) (and os.Mkdir) silently ignore the suid and sgid
|
||||
// bits. We could also silently ignore them but since we have very few
|
||||
// users it seems more prudent to return an error so users notice that
|
||||
// these bits will not be set.
|
||||
if unixMode&^0o1777 != 0 {
|
||||
return nil, fmt.Errorf("%w for mkdir %+.3o: suid and sgid are ignored by mkdir", errInvalidMode, mode)
|
||||
}
|
||||
|
||||
// Try to open as much of the path as possible.
|
||||
currentDir, remainingPath, err := partialLookupInRoot(root, unsafePath)
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
_ = currentDir.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil && !errors.Is(err, unix.ENOENT) {
|
||||
return nil, fmt.Errorf("find existing subpath of %q: %w", unsafePath, err)
|
||||
}
|
||||
|
||||
// If there is an attacker deleting directories as we walk into them,
|
||||
// detect this proactively. Note this is guaranteed to detect if the
|
||||
// attacker deleted any part of the tree up to currentDir.
|
||||
//
|
||||
// Once we walk into a dead directory, partialLookupInRoot would not be
|
||||
// able to walk further down the tree (directories must be empty before
|
||||
// they are deleted), and if the attacker has removed the entire tree we
|
||||
// can be sure that anything that was originally inside a dead directory
|
||||
// must also be deleted and thus is a dead directory in its own right.
|
||||
//
|
||||
// This is mostly a quality-of-life check, because mkdir will simply fail
|
||||
// later if the attacker deletes the tree after this check.
|
||||
if err := fd.IsDeadInode(currentDir); err != nil {
|
||||
return nil, fmt.Errorf("finding existing subpath of %q: %w", unsafePath, err)
|
||||
}
|
||||
|
||||
// Re-open the path to match the O_DIRECTORY reopen loop later (so that we
|
||||
// always return a non-O_PATH handle). We also check that we actually got a
|
||||
// directory.
|
||||
if reopenDir, err := Reopen(currentDir, unix.O_DIRECTORY|unix.O_CLOEXEC); errors.Is(err, unix.ENOTDIR) {
|
||||
return nil, fmt.Errorf("cannot create subdirectories in %q: %w", currentDir.Name(), unix.ENOTDIR)
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("re-opening handle to %q: %w", currentDir.Name(), err)
|
||||
} else { //nolint:revive // indent-error-flow lint doesn't make sense here
|
||||
_ = currentDir.Close()
|
||||
currentDir = reopenDir
|
||||
}
|
||||
|
||||
remainingParts := strings.Split(remainingPath, string(filepath.Separator))
|
||||
if gocompat.SlicesContains(remainingParts, "..") {
|
||||
// The path contained ".." components after the end of the "real"
|
||||
// components. We could try to safely resolve ".." here but that would
|
||||
// add a bunch of extra logic for something that it's not clear even
|
||||
// needs to be supported. So just return an error.
|
||||
//
|
||||
// If we do filepath.Clean(remainingPath) then we end up with the
|
||||
// problem that ".." can erase a trailing dangling symlink and produce
|
||||
// a path that doesn't quite match what the user asked for.
|
||||
return nil, fmt.Errorf("%w: yet-to-be-created path %q contains '..' components", unix.ENOENT, remainingPath)
|
||||
}
|
||||
|
||||
// Create the remaining components.
|
||||
for _, part := range remainingParts {
|
||||
switch part {
|
||||
case "", ".":
|
||||
// Skip over no-op paths.
|
||||
continue
|
||||
}
|
||||
|
||||
// NOTE: mkdir(2) will not follow trailing symlinks, so we can safely
|
||||
// create the final component without worrying about symlink-exchange
|
||||
// attacks.
|
||||
//
|
||||
// If we get -EEXIST, it's possible that another program created the
|
||||
// directory at the same time as us. In that case, just continue on as
|
||||
// if we created it (if the created inode is not a directory, the
|
||||
// following open call will fail).
|
||||
if err := unix.Mkdirat(int(currentDir.Fd()), part, unixMode); err != nil && !errors.Is(err, unix.EEXIST) {
|
||||
err = &os.PathError{Op: "mkdirat", Path: currentDir.Name() + "/" + part, Err: err}
|
||||
// Make the error a bit nicer if the directory is dead.
|
||||
if deadErr := fd.IsDeadInode(currentDir); deadErr != nil {
|
||||
// TODO: Once we bump the minimum Go version to 1.20, we can use
|
||||
// multiple %w verbs for this wrapping. For now we need to use a
|
||||
// compatibility shim for older Go versions.
|
||||
// err = fmt.Errorf("%w (%w)", err, deadErr)
|
||||
err = gocompat.WrapBaseError(err, deadErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get a handle to the next component. O_DIRECTORY means we don't need
|
||||
// to use O_PATH.
|
||||
var nextDir *os.File
|
||||
if linux.HasOpenat2() {
|
||||
nextDir, err = openat2(currentDir, part, &unix.OpenHow{
|
||||
Flags: unix.O_NOFOLLOW | unix.O_DIRECTORY | unix.O_CLOEXEC,
|
||||
Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_SYMLINKS | unix.RESOLVE_NO_XDEV,
|
||||
})
|
||||
} else {
|
||||
nextDir, err = fd.Openat(currentDir, part, unix.O_NOFOLLOW|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = currentDir.Close()
|
||||
currentDir = nextDir
|
||||
|
||||
// It's possible that the directory we just opened was swapped by an
|
||||
// attacker. Unfortunately there isn't much we can do to protect
|
||||
// against this, and MkdirAll's behaviour is that we will reuse
|
||||
// existing directories anyway so the need to protect against this is
|
||||
// incredibly limited (and arguably doesn't even deserve mention here).
|
||||
//
|
||||
// Ideally we might want to check that the owner and mode match what we
|
||||
// would've created -- unfortunately, it is non-trivial to verify that
|
||||
// the owner and mode of the created directory match. While plain Unix
|
||||
// DAC rules seem simple enough to emulate, there are a bunch of other
|
||||
// factors that can change the mode or owner of created directories
|
||||
// (default POSIX ACLs, mount options like uid=1,gid=2,umask=0 on
|
||||
// filesystems like vfat, etc etc). We used to try to verify this but
|
||||
// it just lead to a series of spurious errors.
|
||||
//
|
||||
// We could also check that the directory is non-empty, but
|
||||
// unfortunately some pseduofilesystems (like cgroupfs) create
|
||||
// non-empty directories, which would result in different spurious
|
||||
// errors.
|
||||
}
|
||||
return currentDir, nil
|
||||
}
|
||||
|
||||
// MkdirAll is a race-safe alternative to the [os.MkdirAll] function,
|
||||
// where the new directory is guaranteed to be within the root directory (if an
|
||||
// attacker can move directories from inside the root to outside the root, the
|
||||
// created directory tree might be outside of the root but the key constraint
|
||||
// is that at no point will we walk outside of the directory tree we are
|
||||
// creating).
|
||||
//
|
||||
// Effectively, MkdirAll(root, unsafePath, mode) is equivalent to
|
||||
//
|
||||
// path, _ := securejoin.SecureJoin(root, unsafePath)
|
||||
// err := os.MkdirAll(path, mode)
|
||||
//
|
||||
// But is much safer. The above implementation is unsafe because if an attacker
|
||||
// can modify the filesystem tree between [SecureJoin] and [os.MkdirAll], it is
|
||||
// possible for MkdirAll to resolve unsafe symlink components and create
|
||||
// directories outside of the root.
|
||||
//
|
||||
// If you plan to open the directory after you have created it or want to use
|
||||
// an open directory handle as the root, you should use [MkdirAllHandle] instead.
|
||||
// This function is a wrapper around [MkdirAllHandle].
|
||||
//
|
||||
// [SecureJoin]: https://pkg.go.dev/github.com/cyphar/filepath-securejoin#SecureJoin
|
||||
func MkdirAll(root, unsafePath string, mode os.FileMode) error {
|
||||
rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rootDir.Close() //nolint:errcheck // close failures aren't critical here
|
||||
|
||||
f, err := MkdirAllHandle(rootDir, unsafePath, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = f.Close()
|
||||
return nil
|
||||
}
|
||||
74
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/open_linux.go
generated
vendored
Normal file
74
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/open_linux.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package pathrs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs"
|
||||
)
|
||||
|
||||
// OpenatInRoot is equivalent to [OpenInRoot], except that the root is provided
|
||||
// using an *[os.File] handle, to ensure that the correct root directory is used.
|
||||
func OpenatInRoot(root *os.File, unsafePath string) (*os.File, error) {
|
||||
handle, err := completeLookupInRoot(root, unsafePath)
|
||||
if err != nil {
|
||||
return nil, &os.PathError{Op: "securejoin.OpenInRoot", Path: unsafePath, Err: err}
|
||||
}
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
// OpenInRoot safely opens the provided unsafePath within the root.
|
||||
// Effectively, OpenInRoot(root, unsafePath) is equivalent to
|
||||
//
|
||||
// path, _ := securejoin.SecureJoin(root, unsafePath)
|
||||
// handle, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC)
|
||||
//
|
||||
// But is much safer. The above implementation is unsafe because if an attacker
|
||||
// can modify the filesystem tree between [SecureJoin] and [os.OpenFile], it is
|
||||
// possible for the returned file to be outside of the root.
|
||||
//
|
||||
// Note that the returned handle is an O_PATH handle, meaning that only a very
|
||||
// limited set of operations will work on the handle. This is done to avoid
|
||||
// accidentally opening an untrusted file that could cause issues (such as a
|
||||
// disconnected TTY that could cause a DoS, or some other issue). In order to
|
||||
// use the returned handle, you can "upgrade" it to a proper handle using
|
||||
// [Reopen].
|
||||
//
|
||||
// [SecureJoin]: https://pkg.go.dev/github.com/cyphar/filepath-securejoin#SecureJoin
|
||||
func OpenInRoot(root, unsafePath string) (*os.File, error) {
|
||||
rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rootDir.Close() //nolint:errcheck // close failures aren't critical here
|
||||
return OpenatInRoot(rootDir, unsafePath)
|
||||
}
|
||||
|
||||
// Reopen takes an *[os.File] handle and re-opens it through /proc/self/fd.
|
||||
// Reopen(file, flags) is effectively equivalent to
|
||||
//
|
||||
// fdPath := fmt.Sprintf("/proc/self/fd/%d", file.Fd())
|
||||
// os.OpenFile(fdPath, flags|unix.O_CLOEXEC)
|
||||
//
|
||||
// But with some extra hardenings to ensure that we are not tricked by a
|
||||
// maliciously-configured /proc mount. While this attack scenario is not
|
||||
// common, in container runtimes it is possible for higher-level runtimes to be
|
||||
// tricked into configuring an unsafe /proc that can be used to attack file
|
||||
// operations. See [CVE-2019-19921] for more details.
|
||||
//
|
||||
// [CVE-2019-19921]: https://github.com/advisories/GHSA-fh74-hm69-rqjw
|
||||
func Reopen(handle *os.File, flags int) (*os.File, error) {
|
||||
return procfs.ReopenFd(handle, flags)
|
||||
}
|
||||
101
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/openat2_linux.go
generated
vendored
Normal file
101
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/openat2_linux.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package pathrs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd"
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/procfs"
|
||||
)
|
||||
|
||||
func openat2(dir fd.Fd, path string, how *unix.OpenHow) (*os.File, error) {
|
||||
file, err := fd.Openat2(dir, path, how)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If we are using RESOLVE_IN_ROOT, the name we generated may be wrong.
|
||||
if how.Resolve&unix.RESOLVE_IN_ROOT == unix.RESOLVE_IN_ROOT {
|
||||
if actualPath, err := procfs.ProcSelfFdReadlink(file); err == nil {
|
||||
// TODO: Ideally we would not need to dup the fd, but you cannot
|
||||
// easily just swap an *os.File with one from the same fd
|
||||
// (the GC will close the old one, and you cannot clear the
|
||||
// finaliser easily because it is associated with an internal
|
||||
// field of *os.File not *os.File itself).
|
||||
newFile, err := fd.DupWithName(file, actualPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file = newFile
|
||||
}
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func lookupOpenat2(root fd.Fd, unsafePath string, partial bool) (*os.File, string, error) {
|
||||
if !partial {
|
||||
file, err := openat2(root, unsafePath, &unix.OpenHow{
|
||||
Flags: unix.O_PATH | unix.O_CLOEXEC,
|
||||
Resolve: unix.RESOLVE_IN_ROOT | unix.RESOLVE_NO_MAGICLINKS,
|
||||
})
|
||||
return file, "", err
|
||||
}
|
||||
return partialLookupOpenat2(root, unsafePath)
|
||||
}
|
||||
|
||||
// partialLookupOpenat2 is an alternative implementation of
|
||||
// partialLookupInRoot, using openat2(RESOLVE_IN_ROOT) to more safely get a
|
||||
// handle to the deepest existing child of the requested path within the root.
|
||||
func partialLookupOpenat2(root fd.Fd, unsafePath string) (*os.File, string, error) {
|
||||
// TODO: Implement this as a git-bisect-like binary search.
|
||||
|
||||
unsafePath = filepath.ToSlash(unsafePath) // noop
|
||||
endIdx := len(unsafePath)
|
||||
var lastError error
|
||||
for endIdx > 0 {
|
||||
subpath := unsafePath[:endIdx]
|
||||
|
||||
handle, err := openat2(root, subpath, &unix.OpenHow{
|
||||
Flags: unix.O_PATH | unix.O_CLOEXEC,
|
||||
Resolve: unix.RESOLVE_IN_ROOT | unix.RESOLVE_NO_MAGICLINKS,
|
||||
})
|
||||
if err == nil {
|
||||
// Jump over the slash if we have a non-"" remainingPath.
|
||||
if endIdx < len(unsafePath) {
|
||||
endIdx++
|
||||
}
|
||||
// We found a subpath!
|
||||
return handle, unsafePath[endIdx:], lastError
|
||||
}
|
||||
if errors.Is(err, unix.ENOENT) || errors.Is(err, unix.ENOTDIR) {
|
||||
// That path doesn't exist, let's try the next directory up.
|
||||
endIdx = strings.LastIndexByte(subpath, '/')
|
||||
lastError = err
|
||||
continue
|
||||
}
|
||||
return nil, "", fmt.Errorf("open subpath: %w", err)
|
||||
}
|
||||
// If we couldn't open anything, the whole subpath is missing. Return a
|
||||
// copy of the root fd so that the caller doesn't close this one by
|
||||
// accident.
|
||||
rootClone, err := fd.Dup(root)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return rootClone, unsafePath, lastError
|
||||
}
|
||||
157
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/procfs/procfs_linux.go
generated
vendored
Normal file
157
vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/procfs/procfs_linux.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Copyright (C) 2024-2025 Aleksa Sarai <cyphar@cyphar.com>
|
||||
// Copyright (C) 2024-2025 SUSE LLC
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package procfs provides a safe API for operating on /proc on Linux.
|
||||
package procfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs"
|
||||
)
|
||||
|
||||
// This package mostly just wraps internal/procfs APIs. This is necessary
|
||||
// because we are forced to export some things from internal/procfs in order to
|
||||
// avoid some dependency cycle issues, but we don't want users to see or use
|
||||
// them.
|
||||
|
||||
// ProcThreadSelfCloser is a callback that needs to be called when you are done
|
||||
// operating on an [os.File] fetched using [Handle.OpenThreadSelf].
|
||||
//
|
||||
// [os.File]: https://pkg.go.dev/os#File
|
||||
type ProcThreadSelfCloser = procfs.ProcThreadSelfCloser
|
||||
|
||||
// Handle is a wrapper around an *os.File handle to "/proc", which can be used
|
||||
// to do further procfs-related operations in a safe way.
|
||||
type Handle struct {
|
||||
inner *procfs.Handle
|
||||
}
|
||||
|
||||
// Close close the resources associated with this [Handle]. Note that if this
|
||||
// [Handle] was created with [OpenProcRoot], on some kernels the underlying
|
||||
// procfs handle is cached and so this Close operation may be a no-op. However,
|
||||
// you should always call Close on [Handle]s once you are done with them.
|
||||
func (proc *Handle) Close() error { return proc.inner.Close() }
|
||||
|
||||
// OpenProcRoot tries to open a "safer" handle to "/proc" (i.e., one with the
|
||||
// "subset=pid" mount option applied, available from Linux 5.8). Unless you
|
||||
// plan to do many [Handle.OpenRoot] operations, users should prefer to use
|
||||
// this over [OpenUnsafeProcRoot] which is far more dangerous to keep open.
|
||||
//
|
||||
// If a safe handle cannot be opened, OpenProcRoot will fall back to opening a
|
||||
// regular "/proc" handle.
|
||||
//
|
||||
// Note that using [Handle.OpenRoot] will still work with handles returned by
|
||||
// this function. If a subpath cannot be operated on with a safe "/proc"
|
||||
// handle, then [OpenUnsafeProcRoot] will be called internally and a temporary
|
||||
// unsafe handle will be used.
|
||||
func OpenProcRoot() (*Handle, error) {
|
||||
proc, err := procfs.OpenProcRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Handle{inner: proc}, nil
|
||||
}
|
||||
|
||||
// OpenUnsafeProcRoot opens a handle to "/proc" without any overmounts or
|
||||
// masked paths. You must be extremely careful to make sure this handle is
|
||||
// never leaked to a container and that you program cannot be tricked into
|
||||
// writing to arbitrary paths within it.
|
||||
//
|
||||
// This is not necessary if you just wish to use [Handle.OpenRoot], as handles
|
||||
// returned by [OpenProcRoot] will fall back to using a *temporary* unsafe
|
||||
// handle in that case. You should only really use this if you need to do many
|
||||
// operations with [Handle.OpenRoot] and the performance overhead of making
|
||||
// many procfs handles is an issue. If you do use OpenUnsafeProcRoot, you
|
||||
// should make sure to close the handle as soon as possible to avoid
|
||||
// known-fd-number attacks.
|
||||
func OpenUnsafeProcRoot() (*Handle, error) {
|
||||
proc, err := procfs.OpenUnsafeProcRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Handle{inner: proc}, nil
|
||||
}
|
||||
|
||||
// OpenThreadSelf returns a handle to "/proc/thread-self/<subpath>" (or an
|
||||
// equivalent handle on older kernels where "/proc/thread-self" doesn't exist).
|
||||
// Once finished with the handle, you must call the returned closer function
|
||||
// ([runtime.UnlockOSThread]). You must not pass the returned *os.File to other
|
||||
// Go threads or use the handle after calling the closer.
|
||||
//
|
||||
// [runtime.UnlockOSThread]: https://pkg.go.dev/runtime#UnlockOSThread
|
||||
func (proc *Handle) OpenThreadSelf(subpath string) (*os.File, ProcThreadSelfCloser, error) {
|
||||
return proc.inner.OpenThreadSelf(subpath)
|
||||
}
|
||||
|
||||
// OpenSelf returns a handle to /proc/self/<subpath>.
|
||||
//
|
||||
// Note that in Go programs with non-homogenous threads, this may result in
|
||||
// spurious errors. If you are monkeying around with APIs that are
|
||||
// thread-specific, you probably want to use [Handle.OpenThreadSelf] instead
|
||||
// which will guarantee that the handle refers to the same thread as the caller
|
||||
// is executing on.
|
||||
func (proc *Handle) OpenSelf(subpath string) (*os.File, error) {
|
||||
return proc.inner.OpenSelf(subpath)
|
||||
}
|
||||
|
||||
// OpenRoot returns a handle to /proc/<subpath>.
|
||||
//
|
||||
// You should only use this when you need to operate on global procfs files
|
||||
// (such as sysctls in /proc/sys). Unlike [Handle.OpenThreadSelf],
|
||||
// [Handle.OpenSelf], and [Handle.OpenPid], the procfs handle used internally
|
||||
// for this operation will never use "subset=pid", which makes it a more juicy
|
||||
// target for [CVE-2024-21626]-style attacks (and doing something like opening
|
||||
// a directory with OpenRoot effectively leaks [OpenUnsafeProcRoot] as long as
|
||||
// the file descriptor is open).
|
||||
//
|
||||
// [CVE-2024-21626]: https://github.com/opencontainers/runc/security/advisories/GHSA-xr7r-f8xq-vfvv
|
||||
func (proc *Handle) OpenRoot(subpath string) (*os.File, error) {
|
||||
return proc.inner.OpenRoot(subpath)
|
||||
}
|
||||
|
||||
// OpenPid returns a handle to /proc/$pid/<subpath> (pid can be a pid or tid).
|
||||
// This is mainly intended for usage when operating on other processes.
|
||||
//
|
||||
// You should not use this for the current thread, as special handling is
|
||||
// needed for /proc/thread-self (or /proc/self/task/<tid>) when dealing with
|
||||
// goroutine scheduling -- use [Handle.OpenThreadSelf] instead.
|
||||
//
|
||||
// To refer to the current thread-group, you should use prefer
|
||||
// [Handle.OpenSelf] to passing os.Getpid as the pid argument.
|
||||
func (proc *Handle) OpenPid(pid int, subpath string) (*os.File, error) {
|
||||
return proc.inner.OpenPid(pid, subpath)
|
||||
}
|
||||
|
||||
// ProcSelfFdReadlink gets the real path of the given file by looking at
|
||||
// /proc/self/fd/<fd> with [readlink]. It is effectively just shorthand for
|
||||
// something along the lines of:
|
||||
//
|
||||
// proc, err := procfs.OpenProcRoot()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// link, err := proc.OpenThreadSelf(fmt.Sprintf("fd/%d", f.Fd()))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer link.Close()
|
||||
// var buf [4096]byte
|
||||
// n, err := unix.Readlinkat(int(link.Fd()), "", buf[:])
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// pathname := buf[:n]
|
||||
//
|
||||
// [readlink]: https://pkg.go.dev/golang.org/x/sys/unix#Readlinkat
|
||||
func ProcSelfFdReadlink(f *os.File) (string, error) {
|
||||
return procfs.ProcSelfFdReadlink(f)
|
||||
}
|
||||
12
vendor/github.com/docker/cli/AUTHORS
generated
vendored
12
vendor/github.com/docker/cli/AUTHORS
generated
vendored
@ -63,7 +63,6 @@ Andreas Köhler <andi5.py@gmx.net>
|
||||
Andres G. Aragoneses <knocte@gmail.com>
|
||||
Andres Leon Rangel <aleon1220@gmail.com>
|
||||
Andrew France <andrew@avito.co.uk>
|
||||
Andrew He <he.andrew.mail@gmail.com>
|
||||
Andrew Hsu <andrewhsu@docker.com>
|
||||
Andrew Macpherson <hopscotch23@gmail.com>
|
||||
Andrew McDonnell <bugs@andrewmcdonnell.net>
|
||||
@ -87,12 +86,11 @@ Archimedes Trajano <developer@trajano.net>
|
||||
Arko Dasgupta <arko@tetrate.io>
|
||||
Arnaud Porterie <icecrime@gmail.com>
|
||||
Arnaud Rebillout <elboulangero@gmail.com>
|
||||
Arthur Flageul <arthur.flageul@gmail.com>
|
||||
Arthur Peka <arthur.peka@outlook.com>
|
||||
Ashly Mathew <ashly.mathew@sap.com>
|
||||
Ashwini Oruganti <ashwini.oruganti@gmail.com>
|
||||
Aslam Ahemad <aslamahemad@gmail.com>
|
||||
Austin Vazquez <austin.vazquez@docker.com>
|
||||
Austin Vazquez <austin.vazquez.dev@gmail.com>
|
||||
Azat Khuyiyakhmetov <shadow_uz@mail.ru>
|
||||
Bardia Keyoumarsi <bkeyouma@ucsc.edu>
|
||||
Barnaby Gray <barnaby@pickle.me.uk>
|
||||
@ -137,12 +135,10 @@ Cao Weiwei <cao.weiwei30@zte.com.cn>
|
||||
Carlo Mion <mion00@gmail.com>
|
||||
Carlos Alexandro Becker <caarlos0@gmail.com>
|
||||
Carlos de Paula <me@carlosedp.com>
|
||||
carsontham <carsontham@outlook.com>
|
||||
Carston Schilds <Carston.Schilds@visier.com>
|
||||
Casey Korver <casey@korver.dev>
|
||||
Ce Gao <ce.gao@outlook.com>
|
||||
Cedric Davies <cedricda@microsoft.com>
|
||||
Cesar Talledo <cesar.talledo@docker.com>
|
||||
Cezar Sa Espinola <cezarsa@gmail.com>
|
||||
Chad Faragher <wyckster@hotmail.com>
|
||||
Chao Wang <wangchao.fnst@cn.fujitsu.com>
|
||||
@ -224,7 +220,7 @@ David Alvarez <david.alvarez@flyeralarm.com>
|
||||
David Beitey <david@davidjb.com>
|
||||
David Calavera <david.calavera@gmail.com>
|
||||
David Cramer <davcrame@cisco.com>
|
||||
David Dooling <david.dooling@docker.com>
|
||||
David Dooling <dooling@gmail.com>
|
||||
David Gageot <david@gageot.net>
|
||||
David Karlsson <david.karlsson@docker.com>
|
||||
David le Blanc <systemmonkey42@users.noreply.github.com>
|
||||
@ -269,7 +265,6 @@ Eli Uriegas <eli.uriegas@docker.com>
|
||||
Eli Uriegas <seemethere101@gmail.com>
|
||||
Elias Faxö <elias.faxo@tre.se>
|
||||
Elliot Luo <956941328@qq.com>
|
||||
Eng Zer Jun <engzerjun@gmail.com>
|
||||
Eric Bode <eric.bode@foundries.io>
|
||||
Eric Curtin <ericcurtin17@gmail.com>
|
||||
Eric Engestrom <eric@engestrom.ch>
|
||||
@ -350,7 +345,6 @@ Henning Sprang <henning.sprang@gmail.com>
|
||||
Henry N <henrynmail-github@yahoo.de>
|
||||
Hernan Garcia <hernandanielg@gmail.com>
|
||||
Hongbin Lu <hongbin034@gmail.com>
|
||||
Hossein Abbasi <16090309+hsnabszhdn@users.noreply.github.com>
|
||||
Hu Keping <hukeping@huawei.com>
|
||||
Huayi Zhang <irachex@gmail.com>
|
||||
Hugo Chastel <Hugo-C@users.noreply.github.com>
|
||||
@ -601,7 +595,6 @@ Michael Prokop <github@michael-prokop.at>
|
||||
Michael Scharf <github@scharf.gr>
|
||||
Michael Spetsiotis <michael_spets@hotmail.com>
|
||||
Michael Steinert <mike.steinert@gmail.com>
|
||||
Michael Tews <michael@tews.dev>
|
||||
Michael West <mwest@mdsol.com>
|
||||
Michal Minář <miminar@redhat.com>
|
||||
Michał Czeraszkiewicz <czerasz@gmail.com>
|
||||
@ -903,7 +896,6 @@ Wenlong Zhang <zhangwenlong@loongson.cn>
|
||||
Wenzhi Liang <wenzhi.liang@gmail.com>
|
||||
Wes Morgan <cap10morgan@gmail.com>
|
||||
Wewang Xiaorenfine <wang.xiaoren@zte.com.cn>
|
||||
Will Wang <willww64@gmail.com>
|
||||
William Henry <whenry@redhat.com>
|
||||
Xianglin Gao <xlgao@zju.edu.cn>
|
||||
Xiaodong Liu <liuxiaodong@loongson.cn>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user