mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Bump to Buildah v1.37.0
Bump Buidah to v1.37.0 Signed-off-by: tomsweeneyredhat <tsweeney@redhat.com>
This commit is contained in:
5
vendor/github.com/openshift/imagebuilder/.travis.yml
generated
vendored
5
vendor/github.com/openshift/imagebuilder/.travis.yml
generated
vendored
@ -14,8 +14,13 @@ before_install:
|
||||
- yes | sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||
- sudo apt-get update -q -y
|
||||
- sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
|
||||
- sudo systemctl enable --now docker.service && sudo systemctl enable --now docker.socket
|
||||
- docker pull busybox
|
||||
- docker pull centos:7
|
||||
- docker pull alpine
|
||||
- docker pull registry.fedoraproject.org/fedora-minimal
|
||||
- docker pull registry.fedoraproject.org/fedora-minimal:41-x86_64
|
||||
- docker pull registry.fedoraproject.org/fedora-minimal:41-aarch64
|
||||
- chmod -R go-w ./dockerclient/testdata
|
||||
|
||||
script:
|
||||
|
4
vendor/github.com/openshift/imagebuilder/README.md
generated
vendored
4
vendor/github.com/openshift/imagebuilder/README.md
generated
vendored
@ -103,7 +103,11 @@ Example of usage from OpenShift's experimental `dockerbuild` [command with mount
|
||||
|
||||
```
|
||||
docker rmi busybox; docker pull busybox
|
||||
docker rmi alpine; docker pull alpine
|
||||
docker rmi centos:7; docker pull centos:7
|
||||
docker rmi registry.fedoraproject.org/fedora-minimal; docker pull registry.fedoraproject.org/fedora-minimal
|
||||
docker rmi registry.fedoraproject.org/fedora-minimal:41-x86_64; docker pull registry.fedoraproject.org/fedora-minimal:41-x86_64
|
||||
docker rmi registry.fedoraproject.org/fedora-minimal:41-aarch64; docker pull registry.fedoraproject.org/fedora-minimal:41-aarch64
|
||||
chmod -R go-w ./dockerclient/testdata
|
||||
go test ./dockerclient -tags conformance -timeout 30m
|
||||
```
|
||||
|
115
vendor/github.com/openshift/imagebuilder/builder.go
generated
vendored
115
vendor/github.com/openshift/imagebuilder/builder.go
generated
vendored
@ -288,29 +288,38 @@ type Stage struct {
|
||||
|
||||
func NewStages(node *parser.Node, b *Builder) (Stages, error) {
|
||||
var stages Stages
|
||||
var allDeclaredArgs []string
|
||||
for _, root := range SplitBy(node, command.Arg) {
|
||||
argNode := root.Children[0]
|
||||
if argNode.Value == command.Arg {
|
||||
// extract declared variable
|
||||
s := strings.SplitN(argNode.Original, " ", 2)
|
||||
if len(s) == 2 && (strings.ToLower(s[0]) == command.Arg) {
|
||||
allDeclaredArgs = append(allDeclaredArgs, s[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
var headingArgs []string
|
||||
if err := b.extractHeadingArgsFromNode(node); err != nil {
|
||||
return stages, err
|
||||
}
|
||||
for k := range b.HeadingArgs {
|
||||
headingArgs = append(headingArgs, k)
|
||||
}
|
||||
for i, root := range SplitBy(node, command.From) {
|
||||
name, _ := extractNameFromNode(root.Children[0])
|
||||
if len(name) == 0 {
|
||||
name = strconv.Itoa(i)
|
||||
}
|
||||
filteredUserArgs := make(map[string]string)
|
||||
for k, v := range b.UserArgs {
|
||||
for _, a := range b.GlobalAllowedArgs {
|
||||
if a == k {
|
||||
filteredUserArgs[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
userArgs := envMapAsSlice(filteredUserArgs)
|
||||
userArgs = mergeEnv(envMapAsSlice(b.BuiltinArgDefaults), userArgs)
|
||||
userArgs = mergeEnv(envMapAsSlice(builtinArgDefaults), userArgs)
|
||||
userArgs = mergeEnv(envMapAsSlice(b.HeadingArgs), userArgs)
|
||||
processedName, err := ProcessWord(name, userArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stages = append(stages, Stage{
|
||||
Position: i,
|
||||
Name: name,
|
||||
Builder: b.builderForStage(allDeclaredArgs),
|
||||
Name: processedName,
|
||||
Builder: b.builderForStage(headingArgs),
|
||||
Node: root,
|
||||
})
|
||||
}
|
||||
@ -375,32 +384,41 @@ func extractNameFromNode(node *parser.Node) (string, bool) {
|
||||
}
|
||||
|
||||
func (b *Builder) builderForStage(globalArgsList []string) *Builder {
|
||||
stageBuilder := newBuilderWithGlobalAllowedArgs(b.UserArgs, globalArgsList)
|
||||
for k, v := range b.HeadingArgs {
|
||||
stageBuilder.HeadingArgs[k] = v
|
||||
}
|
||||
stageBuilder := newBuilderWithGlobalAllowedArgs(b.UserArgs, b.HeadingArgs, b.BuiltinArgDefaults, globalArgsList)
|
||||
return stageBuilder
|
||||
}
|
||||
|
||||
type Builder struct {
|
||||
RunConfig docker.Config
|
||||
|
||||
Env []string
|
||||
Args map[string]string
|
||||
HeadingArgs map[string]string
|
||||
UserArgs map[string]string
|
||||
CmdSet bool
|
||||
Author string
|
||||
// Certain instructions like `FROM` will need to use
|
||||
// `ARG` decalred before or not in this stage hence
|
||||
// while processing instruction like `FROM ${SOME_ARG}`
|
||||
// we will make sure to verify if they are declared any
|
||||
// where in containerfile or not.
|
||||
GlobalAllowedArgs []string
|
||||
Env []string
|
||||
|
||||
// Args contains values originally given to NewBuilder() or set due to
|
||||
// ARG instructions in a stage, either with a default value provided,
|
||||
// or with a default inherited from an ARG instruction in the header
|
||||
Args map[string]string
|
||||
// HeadingArgs contains the values for ARG instructions in the
|
||||
// Dockerfile which occurred before the first FROM instruction, either
|
||||
// with a default value provided as part of the ARG instruction, or
|
||||
// expecting a value to be supplied in UserArgs via NewBuilder().
|
||||
HeadingArgs map[string]string
|
||||
// UserArgs includes a copy of the values that were passed to
|
||||
// NewBuilder(), unmodified.
|
||||
UserArgs map[string]string
|
||||
|
||||
CmdSet bool
|
||||
Author string
|
||||
|
||||
// GlobalAllowedArgs are args which should be resolvable in a FROM
|
||||
// instruction, either built-in and always available, or introduced by
|
||||
// an ARG instruction in the header.
|
||||
GlobalAllowedArgs []string
|
||||
// AllowedArgs are args which should be resolvable in this stage,
|
||||
// having been introduced by a previous ARG instruction in this stage.
|
||||
AllowedArgs map[string]bool
|
||||
Volumes VolumeSet
|
||||
Excludes []string
|
||||
|
||||
Volumes VolumeSet
|
||||
Excludes []string
|
||||
|
||||
PendingVolumes VolumeSet
|
||||
PendingRuns []Run
|
||||
@ -410,13 +428,18 @@ type Builder struct {
|
||||
// Raw platform string specified with `FROM --platform` of the stage
|
||||
// It's up to the implementation or client to parse and use this field
|
||||
Platform string
|
||||
|
||||
// Overrides for TARGET... and BUILD... values. TARGET... values are
|
||||
// typically only necessary if the builder's target platform is not the
|
||||
// same as the build platform.
|
||||
BuiltinArgDefaults map[string]string
|
||||
}
|
||||
|
||||
func NewBuilder(args map[string]string) *Builder {
|
||||
return newBuilderWithGlobalAllowedArgs(args, []string{})
|
||||
return newBuilderWithGlobalAllowedArgs(args, nil, nil, []string{})
|
||||
}
|
||||
|
||||
func newBuilderWithGlobalAllowedArgs(args map[string]string, globalallowedargs []string) *Builder {
|
||||
func newBuilderWithGlobalAllowedArgs(args, headingArgs, userBuiltinArgDefaults map[string]string, globalAllowedArgs []string) *Builder {
|
||||
allowed := make(map[string]bool)
|
||||
for k, v := range builtinAllowedBuildArgs {
|
||||
allowed[k] = v
|
||||
@ -427,12 +450,28 @@ func newBuilderWithGlobalAllowedArgs(args map[string]string, globalallowedargs [
|
||||
userArgs[k] = v
|
||||
initialArgs[k] = v
|
||||
}
|
||||
var copiedGlobalAllowedArgs []string
|
||||
if len(globalAllowedArgs) > 0 {
|
||||
copiedGlobalAllowedArgs = append([]string{}, globalAllowedArgs...)
|
||||
}
|
||||
copiedHeadingArgs := make(map[string]string)
|
||||
for k, v := range headingArgs {
|
||||
copiedHeadingArgs[k] = v
|
||||
}
|
||||
copiedBuiltinArgDefaults := make(map[string]string)
|
||||
for k, v := range builtinArgDefaults {
|
||||
copiedBuiltinArgDefaults[k] = v
|
||||
}
|
||||
for k, v := range userBuiltinArgDefaults {
|
||||
copiedBuiltinArgDefaults[k] = v
|
||||
}
|
||||
return &Builder{
|
||||
Args: initialArgs,
|
||||
UserArgs: userArgs,
|
||||
HeadingArgs: make(map[string]string),
|
||||
AllowedArgs: allowed,
|
||||
GlobalAllowedArgs: globalallowedargs,
|
||||
Args: initialArgs,
|
||||
UserArgs: userArgs,
|
||||
HeadingArgs: copiedHeadingArgs,
|
||||
AllowedArgs: allowed,
|
||||
GlobalAllowedArgs: copiedGlobalAllowedArgs,
|
||||
BuiltinArgDefaults: copiedBuiltinArgDefaults,
|
||||
}
|
||||
}
|
||||
|
||||
|
124
vendor/github.com/openshift/imagebuilder/dispatchers.go
generated
vendored
124
vendor/github.com/openshift/imagebuilder/dispatchers.go
generated
vendored
@ -18,6 +18,7 @@ import (
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containers/storage/pkg/regexp"
|
||||
"github.com/openshift/imagebuilder/signal"
|
||||
@ -35,7 +36,7 @@ var (
|
||||
var localspec = platforms.DefaultSpec()
|
||||
|
||||
// https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
|
||||
var builtinBuildArgs = map[string]string{
|
||||
var builtinArgDefaults = map[string]string{
|
||||
"TARGETPLATFORM": localspec.OS + "/" + localspec.Architecture,
|
||||
"TARGETOS": localspec.OS,
|
||||
"TARGETARCH": localspec.Architecture,
|
||||
@ -48,8 +49,8 @@ var builtinBuildArgs = map[string]string{
|
||||
|
||||
func init() {
|
||||
if localspec.Variant != "" {
|
||||
builtinBuildArgs["TARGETPLATFORM"] = builtinBuildArgs["TARGETPLATFORM"] + "/" + localspec.Variant
|
||||
builtinBuildArgs["BUILDPLATFORM"] = builtinBuildArgs["BUILDPLATFORM"] + "/" + localspec.Variant
|
||||
builtinArgDefaults["TARGETPLATFORM"] = builtinArgDefaults["TARGETPLATFORM"] + "/" + localspec.Variant
|
||||
builtinArgDefaults["BUILDPLATFORM"] = builtinArgDefaults["BUILDPLATFORM"] + "/" + localspec.Variant
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,13 +84,12 @@ func env(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
|
||||
fmt.Printf("Str1:%v\n", flStr1)
|
||||
*/
|
||||
|
||||
for j := 0; j < len(args); j++ {
|
||||
for j := 0; j+1 < len(args); j += 2 {
|
||||
// name ==> args[j]
|
||||
// value ==> args[j+1]
|
||||
newVar := []string{args[j] + "=" + args[j+1]}
|
||||
b.RunConfig.Env = mergeEnv(b.RunConfig.Env, newVar)
|
||||
b.Env = mergeEnv(b.Env, newVar)
|
||||
j++
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -254,7 +254,13 @@ func dispatchCopy(b *Builder, args []string, attributes map[string]bool, flagArg
|
||||
var link bool
|
||||
var parents bool
|
||||
var excludes []string
|
||||
userArgs := mergeEnv(envMapAsSlice(b.Args), b.Env)
|
||||
filteredUserArgs := make(map[string]string)
|
||||
for k, v := range b.Args {
|
||||
if _, ok := b.AllowedArgs[k]; ok {
|
||||
filteredUserArgs[k] = v
|
||||
}
|
||||
}
|
||||
userArgs := mergeEnv(envMapAsSlice(filteredUserArgs), b.Env)
|
||||
for _, a := range flagArgs {
|
||||
arg, err := ProcessWord(a, userArgs)
|
||||
if err != nil {
|
||||
@ -323,17 +329,12 @@ func from(b *Builder, args []string, attributes map[string]bool, flagArgs []stri
|
||||
case len(args) == 3 && len(args[0]) > 0 && strings.EqualFold(args[1], "as") && len(args[2]) > 0:
|
||||
|
||||
default:
|
||||
return fmt.Errorf("FROM requires either one argument, or three: FROM <source> [as <name>]")
|
||||
return fmt.Errorf("FROM requires either one argument, or three: FROM <source> [AS <name>]")
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
|
||||
// Support ARG before from
|
||||
argStrs := []string{}
|
||||
for n, v := range b.HeadingArgs {
|
||||
argStrs = append(argStrs, n+"="+v)
|
||||
}
|
||||
defaultArgs := envMapAsSlice(builtinBuildArgs)
|
||||
// Support ARG before FROM
|
||||
filteredUserArgs := make(map[string]string)
|
||||
for k, v := range b.UserArgs {
|
||||
for _, a := range b.GlobalAllowedArgs {
|
||||
@ -343,10 +344,11 @@ func from(b *Builder, args []string, attributes map[string]bool, flagArgs []stri
|
||||
}
|
||||
}
|
||||
userArgs := mergeEnv(envMapAsSlice(filteredUserArgs), b.Env)
|
||||
userArgs = mergeEnv(defaultArgs, userArgs)
|
||||
nameArgs := mergeEnv(argStrs, userArgs)
|
||||
userArgs = mergeEnv(envMapAsSlice(b.BuiltinArgDefaults), userArgs)
|
||||
userArgs = mergeEnv(envMapAsSlice(builtinArgDefaults), userArgs)
|
||||
userArgs = mergeEnv(envMapAsSlice(b.HeadingArgs), userArgs)
|
||||
var err error
|
||||
if name, err = ProcessWord(name, nameArgs); err != nil {
|
||||
if name, err = ProcessWord(name, userArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -357,7 +359,7 @@ func from(b *Builder, args []string, attributes map[string]bool, flagArgs []stri
|
||||
}
|
||||
}
|
||||
for _, a := range flagArgs {
|
||||
arg, err := ProcessWord(a, nameArgs)
|
||||
arg, err := ProcessWord(a, userArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -727,70 +729,64 @@ func healthcheck(b *Builder, args []string, attributes map[string]bool, flagArgs
|
||||
return nil
|
||||
}
|
||||
|
||||
var targetArgs = []string{"TARGETOS", "TARGETARCH", "TARGETVARIANT"}
|
||||
|
||||
// ARG name[=value]
|
||||
//
|
||||
// Adds the variable foo to the trusted list of variables that can be passed
|
||||
// to builder using the --build-arg flag for expansion/subsitution or passing to 'run'.
|
||||
// Dockerfile author may optionally set a default value of this variable.
|
||||
func arg(b *Builder, args []string, attributes map[string]bool, flagArgs []string, original string, heredocs []buildkitparser.Heredoc) error {
|
||||
var (
|
||||
name string
|
||||
value string
|
||||
hasDefault bool
|
||||
)
|
||||
|
||||
for _, argument := range args {
|
||||
var (
|
||||
name string
|
||||
defaultValue string
|
||||
haveDefault bool
|
||||
)
|
||||
arg := argument
|
||||
// 'arg' can just be a name or name-value pair. Note that this is different
|
||||
// from 'env' that handles the split of name and value at the parser level.
|
||||
// The reason for doing it differently for 'arg' is that we support just
|
||||
// defining an arg and not assign it a value (while 'env' always expects a
|
||||
// defining an arg without assigning it a value (while 'env' always expects a
|
||||
// name-value pair). If possible, it will be good to harmonize the two.
|
||||
if strings.Contains(arg, "=") {
|
||||
parts := strings.SplitN(arg, "=", 2)
|
||||
name = parts[0]
|
||||
value = parts[1]
|
||||
hasDefault = true
|
||||
if name == "TARGETPLATFORM" {
|
||||
p, err := platforms.Parse(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing TARGETPLATFORM argument")
|
||||
}
|
||||
for _, val := range targetArgs {
|
||||
b.AllowedArgs[val] = true
|
||||
}
|
||||
b.Args["TARGETPLATFORM"] = p.OS + "/" + p.Architecture
|
||||
b.Args["TARGETOS"] = p.OS
|
||||
b.Args["TARGETARCH"] = p.Architecture
|
||||
b.Args["TARGETVARIANT"] = p.Variant
|
||||
if p.Variant != "" {
|
||||
b.Args["TARGETPLATFORM"] = b.Args["TARGETPLATFORM"] + "/" + p.Variant
|
||||
}
|
||||
}
|
||||
} else if val, ok := builtinBuildArgs[arg]; ok {
|
||||
name = arg
|
||||
value = val
|
||||
hasDefault = true
|
||||
} else {
|
||||
name = arg
|
||||
hasDefault = false
|
||||
}
|
||||
name, defaultValue, haveDefault = strings.Cut(arg, "=")
|
||||
|
||||
// add the arg to allowed list of build-time args from this step on.
|
||||
b.AllowedArgs[name] = true
|
||||
|
||||
// If there is still no default value, a value can be assigned from the heading args
|
||||
if val, ok := b.HeadingArgs[name]; ok && !hasDefault {
|
||||
b.Args[name] = val
|
||||
// If the stage introduces one of the predefined args, add the
|
||||
// predefined value to the list of values known in this stage
|
||||
if value, defined := builtinArgDefaults[name]; defined {
|
||||
if haveDefault && (name == "TARGETPLATFORM" || name == "BUILDPLATFORM") {
|
||||
return fmt.Errorf("attempted to redefine %q: %w", name, errdefs.ErrInvalidArgument)
|
||||
}
|
||||
if b.BuiltinArgDefaults == nil {
|
||||
b.BuiltinArgDefaults = make(map[string]string)
|
||||
}
|
||||
// N.B.: we're only consulting b.BuiltinArgDefaults for
|
||||
// values that correspond to keys in
|
||||
// builtinArgDefaults, which keeps the caller from
|
||||
// using it to sneak in arbitrary ARG values
|
||||
if _, setByUser := b.UserArgs[name]; !setByUser && defined {
|
||||
if builderValue, builderDefined := b.BuiltinArgDefaults[name]; builderDefined {
|
||||
b.Args[name] = builderValue
|
||||
} else {
|
||||
b.Args[name] = value
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// If there is a default value associated with this arg then add it to the
|
||||
// b.buildArgs, later default values for the same arg override earlier ones.
|
||||
// The args passed to builder (UserArgs) override the default value of 'arg'
|
||||
// Don't add them here as they were already set in NewBuilder.
|
||||
if _, ok := b.UserArgs[name]; !ok && hasDefault {
|
||||
b.Args[name] = value
|
||||
// If there is still no default value, check for a default value from the heading args
|
||||
if !haveDefault {
|
||||
defaultValue, haveDefault = b.HeadingArgs[name]
|
||||
}
|
||||
|
||||
// If there is a default value provided for this arg, and the user didn't supply
|
||||
// a value, then set the default value in b.Args. Later defaults given for the
|
||||
// same arg override earlier ones. The args passed to the builder (UserArgs) override
|
||||
// any default values of 'arg', so don't set them here as they were already set
|
||||
// in NewBuilder().
|
||||
if _, setByUser := b.UserArgs[name]; !setByUser && haveDefault {
|
||||
b.Args[name] = defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/openshift/imagebuilder/imagebuilder.spec
generated
vendored
2
vendor/github.com/openshift/imagebuilder/imagebuilder.spec
generated
vendored
@ -12,7 +12,7 @@
|
||||
#
|
||||
|
||||
%global golang_version 1.19
|
||||
%{!?version: %global version 1.2.11}
|
||||
%{!?version: %global version 1.2.14}
|
||||
%{!?release: %global release 1}
|
||||
%global package_name imagebuilder
|
||||
%global product_name Container Image Builder
|
||||
|
142
vendor/github.com/openshift/imagebuilder/shell_parser.go
generated
vendored
142
vendor/github.com/openshift/imagebuilder/shell_parser.go
generated
vendored
@ -9,6 +9,7 @@ package imagebuilder
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
"unicode"
|
||||
@ -103,9 +104,14 @@ func (w *wordsStruct) getWords() []string {
|
||||
return w.words
|
||||
}
|
||||
|
||||
func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||
_, result, words, err := sw.processStopOnAny([]rune{stopChar})
|
||||
return result, words, err
|
||||
}
|
||||
|
||||
// Process the word, starting at 'pos', and stop when we get to the
|
||||
// end of the word or the 'stopChar' character
|
||||
func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||
func (sw *shellWord) processStopOnAny(stopChars []rune) (rune, string, []string, error) {
|
||||
var result string
|
||||
var words wordsStruct
|
||||
|
||||
@ -115,18 +121,26 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||
'$': sw.processDollar,
|
||||
}
|
||||
|
||||
sliceContains := func(slice []rune, value rune) bool {
|
||||
for _, r := range slice {
|
||||
if r == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for sw.scanner.Peek() != scanner.EOF {
|
||||
ch := sw.scanner.Peek()
|
||||
|
||||
if stopChar != scanner.EOF && ch == stopChar {
|
||||
sw.scanner.Next()
|
||||
return result, words.getWords(), nil
|
||||
if sliceContains(stopChars, ch) {
|
||||
sw.scanner.Next() // skip over ch
|
||||
return ch, result, words.getWords(), nil
|
||||
}
|
||||
if fn, ok := charFuncMapping[ch]; ok {
|
||||
// Call special processing func for certain chars
|
||||
tmp, err := fn()
|
||||
if err != nil {
|
||||
return "", []string{}, err
|
||||
return ch, "", []string{}, err
|
||||
}
|
||||
result += tmp
|
||||
|
||||
@ -157,11 +171,11 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if stopChar != scanner.EOF {
|
||||
return "", []string{}, fmt.Errorf("unexpected end of statement while looking for matching %s", string(stopChar))
|
||||
if !sliceContains(stopChars, scanner.EOF) {
|
||||
return scanner.EOF, "", []string{}, fmt.Errorf("unexpected end of statement while looking for matching %s", string(stopChars))
|
||||
}
|
||||
|
||||
return result, words.getWords(), nil
|
||||
return scanner.EOF, result, words.getWords(), nil
|
||||
}
|
||||
|
||||
func (sw *shellWord) processSingleQuote() (string, error) {
|
||||
@ -281,7 +295,117 @@ func (sw *shellWord) processDollar() (string, error) {
|
||||
return "", fmt.Errorf("Unsupported modifier (%c) in substitution: %s", modifier, sw.word)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("Missing ':' in substitution: %s", sw.word)
|
||||
if ch == '#' || ch == '%' { // strip a prefix or suffix
|
||||
sw.scanner.Next() // skip over # or %
|
||||
greedy := false
|
||||
if sw.scanner.Peek() == ch {
|
||||
sw.scanner.Next() // skip over second # or %
|
||||
greedy = true
|
||||
}
|
||||
word, _, err := sw.processStopOn('}')
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
value := sw.getEnv(name)
|
||||
switch ch {
|
||||
case '#': // strip a prefix
|
||||
if word == "" {
|
||||
return "", fmt.Errorf("%s#: no prefix to remove", name)
|
||||
}
|
||||
if greedy {
|
||||
for i := len(value) - 1; i >= 0; i-- {
|
||||
if matches, err := path.Match(word, value[:i]); err == nil && matches {
|
||||
return value[i:], nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < len(value)-1; i++ {
|
||||
if matches, err := path.Match(word, value[:i]); err == nil && matches {
|
||||
return value[i:], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
case '%': // strip a suffix
|
||||
if word == "" {
|
||||
return "", fmt.Errorf("%s%%: no suffix to remove", name)
|
||||
}
|
||||
if greedy {
|
||||
for i := 0; i < len(value)-1; i++ {
|
||||
if matches, err := path.Match(word, value[i:]); err == nil && matches {
|
||||
return value[:i], nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := len(value) - 1; i >= 0; i-- {
|
||||
if matches, err := path.Match(word, value[i:]); err == nil && matches {
|
||||
return value[:i], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
if ch == '/' { // perform substitution
|
||||
sw.scanner.Next() // skip over /
|
||||
all, begin, end := false, false, false
|
||||
switch sw.scanner.Peek() {
|
||||
case ch:
|
||||
sw.scanner.Next() // skip over second /
|
||||
all = true // replace all instances
|
||||
case '#':
|
||||
sw.scanner.Next() // skip over #
|
||||
begin = true // replace only an prefix instance
|
||||
case '%':
|
||||
sw.scanner.Next() // skip over %
|
||||
end = true // replace only a fuffix instance
|
||||
}
|
||||
// the '/', and the replacement pattern that follows
|
||||
// it, can be omitted if the replacement pattern is "",
|
||||
// so the pattern-to-replace can end at either a '/' or
|
||||
// a '}'
|
||||
ch, pattern, _, err := sw.processStopOnAny([]rune{'/', '}'})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if pattern == "" { // pattern to replace needs to not be empty
|
||||
return "", fmt.Errorf("%s/: no pattern to replace", name)
|
||||
}
|
||||
var replacement string
|
||||
if ch == '/' { // patter to replace it with was specified
|
||||
replacement, _, err = sw.processStopOn('}')
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
value := sw.getEnv(name)
|
||||
i := 0
|
||||
for {
|
||||
if i >= len(value) {
|
||||
break
|
||||
}
|
||||
for j := len(value); j > i; j-- {
|
||||
if begin && i != 0 {
|
||||
continue
|
||||
}
|
||||
if end && j != len(value) {
|
||||
continue
|
||||
}
|
||||
matches, err := path.Match(pattern, value[i:j])
|
||||
if err == nil && matches {
|
||||
value = value[:i] + replacement + value[j:]
|
||||
if !all {
|
||||
return value, nil
|
||||
}
|
||||
i += (len(replacement) - 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
return "", fmt.Errorf("Missing ':' or '#' or '%%' or '/' in substitution: %s", sw.word)
|
||||
}
|
||||
// $xxx case
|
||||
name := sw.processName()
|
||||
|
Reference in New Issue
Block a user