mirror of
				https://github.com/containers/podman.git
				synced 2025-11-01 02:42:11 +08:00 
			
		
		
		
	![dependabot[bot]](/assets/img/avatar_default.png) b5890fc86b
			
		
	
	b5890fc86b
	
	
	
		
			
			Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.32.1 to 1.32.2. - [Release notes](https://github.com/containers/storage/releases) - [Changelog](https://github.com/containers/storage/blob/master/docs/containers-storage-changes.md) - [Commits](https://github.com/containers/storage/compare/v1.32.1...v1.32.2) --- updated-dependencies: - dependency-name: github.com/containers/storage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
		
			
				
	
	
		
			318 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			318 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package shellwords
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"errors"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 	"unicode"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	ParseEnv      bool = false
 | |
| 	ParseBacktick bool = false
 | |
| )
 | |
| 
 | |
| func isSpace(r rune) bool {
 | |
| 	switch r {
 | |
| 	case ' ', '\t', '\r', '\n':
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func replaceEnv(getenv func(string) string, s string) string {
 | |
| 	if getenv == nil {
 | |
| 		getenv = os.Getenv
 | |
| 	}
 | |
| 
 | |
| 	var buf bytes.Buffer
 | |
| 	rs := []rune(s)
 | |
| 	for i := 0; i < len(rs); i++ {
 | |
| 		r := rs[i]
 | |
| 		if r == '\\' {
 | |
| 			i++
 | |
| 			if i == len(rs) {
 | |
| 				break
 | |
| 			}
 | |
| 			buf.WriteRune(rs[i])
 | |
| 			continue
 | |
| 		} else if r == '$' {
 | |
| 			i++
 | |
| 			if i == len(rs) {
 | |
| 				buf.WriteRune(r)
 | |
| 				break
 | |
| 			}
 | |
| 			if rs[i] == 0x7b {
 | |
| 				i++
 | |
| 				p := i
 | |
| 				for ; i < len(rs); i++ {
 | |
| 					r = rs[i]
 | |
| 					if r == '\\' {
 | |
| 						i++
 | |
| 						if i == len(rs) {
 | |
| 							return s
 | |
| 						}
 | |
| 						continue
 | |
| 					}
 | |
| 					if r == 0x7d || (!unicode.IsLetter(r) && r != '_' && !unicode.IsDigit(r)) {
 | |
| 						break
 | |
| 					}
 | |
| 				}
 | |
| 				if r != 0x7d {
 | |
| 					return s
 | |
| 				}
 | |
| 				if i > p {
 | |
| 					buf.WriteString(getenv(s[p:i]))
 | |
| 				}
 | |
| 			} else {
 | |
| 				p := i
 | |
| 				for ; i < len(rs); i++ {
 | |
| 					r := rs[i]
 | |
| 					if r == '\\' {
 | |
| 						i++
 | |
| 						if i == len(rs) {
 | |
| 							return s
 | |
| 						}
 | |
| 						continue
 | |
| 					}
 | |
| 					if !unicode.IsLetter(r) && r != '_' && !unicode.IsDigit(r) {
 | |
| 						break
 | |
| 					}
 | |
| 				}
 | |
| 				if i > p {
 | |
| 					buf.WriteString(getenv(s[p:i]))
 | |
| 					i--
 | |
| 				} else {
 | |
| 					buf.WriteString(s[p:])
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			buf.WriteRune(r)
 | |
| 		}
 | |
| 	}
 | |
| 	return buf.String()
 | |
| }
 | |
| 
 | |
| type Parser struct {
 | |
| 	ParseEnv      bool
 | |
| 	ParseBacktick bool
 | |
| 	Position      int
 | |
| 	Dir           string
 | |
| 
 | |
| 	// If ParseEnv is true, use this for getenv.
 | |
| 	// If nil, use os.Getenv.
 | |
| 	Getenv func(string) string
 | |
| }
 | |
| 
 | |
| func NewParser() *Parser {
 | |
| 	return &Parser{
 | |
| 		ParseEnv:      ParseEnv,
 | |
| 		ParseBacktick: ParseBacktick,
 | |
| 		Position:      0,
 | |
| 		Dir:           "",
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type argType int
 | |
| 
 | |
| const (
 | |
| 	argNo argType = iota
 | |
| 	argSingle
 | |
| 	argQuoted
 | |
| )
 | |
| 
 | |
| func (p *Parser) Parse(line string) ([]string, error) {
 | |
| 	args := []string{}
 | |
| 	buf := ""
 | |
| 	var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool
 | |
| 	backtick := ""
 | |
| 
 | |
| 	pos := -1
 | |
| 	got := argNo
 | |
| 
 | |
| 	i := -1
 | |
| loop:
 | |
| 	for _, r := range line {
 | |
| 		i++
 | |
| 		if escaped {
 | |
| 			buf += string(r)
 | |
| 			escaped = false
 | |
| 			got = argSingle
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if r == '\\' {
 | |
| 			if singleQuoted {
 | |
| 				buf += string(r)
 | |
| 			} else {
 | |
| 				escaped = true
 | |
| 			}
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if isSpace(r) {
 | |
| 			if singleQuoted || doubleQuoted || backQuote || dollarQuote {
 | |
| 				buf += string(r)
 | |
| 				backtick += string(r)
 | |
| 			} else if got != argNo {
 | |
| 				if p.ParseEnv {
 | |
| 					if got == argSingle {
 | |
| 						parser := &Parser{ParseEnv: false, ParseBacktick: false, Position: 0, Dir: p.Dir}
 | |
| 						strs, err := parser.Parse(replaceEnv(p.Getenv, buf))
 | |
| 						if err != nil {
 | |
| 							return nil, err
 | |
| 						}
 | |
| 						args = append(args, strs...)
 | |
| 					} else {
 | |
| 						args = append(args, replaceEnv(p.Getenv, buf))
 | |
| 					}
 | |
| 				} else {
 | |
| 					args = append(args, buf)
 | |
| 				}
 | |
| 				buf = ""
 | |
| 				got = argNo
 | |
| 			}
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		switch r {
 | |
| 		case '`':
 | |
| 			if !singleQuoted && !doubleQuoted && !dollarQuote {
 | |
| 				if p.ParseBacktick {
 | |
| 					if backQuote {
 | |
| 						out, err := shellRun(backtick, p.Dir)
 | |
| 						if err != nil {
 | |
| 							return nil, err
 | |
| 						}
 | |
| 						buf = buf[:len(buf)-len(backtick)] + out
 | |
| 					}
 | |
| 					backtick = ""
 | |
| 					backQuote = !backQuote
 | |
| 					continue
 | |
| 				}
 | |
| 				backtick = ""
 | |
| 				backQuote = !backQuote
 | |
| 			}
 | |
| 		case ')':
 | |
| 			if !singleQuoted && !doubleQuoted && !backQuote {
 | |
| 				if p.ParseBacktick {
 | |
| 					if dollarQuote {
 | |
| 						out, err := shellRun(backtick, p.Dir)
 | |
| 						if err != nil {
 | |
| 							return nil, err
 | |
| 						}
 | |
| 						buf = buf[:len(buf)-len(backtick)-2] + out
 | |
| 					}
 | |
| 					backtick = ""
 | |
| 					dollarQuote = !dollarQuote
 | |
| 					continue
 | |
| 				}
 | |
| 				backtick = ""
 | |
| 				dollarQuote = !dollarQuote
 | |
| 			}
 | |
| 		case '(':
 | |
| 			if !singleQuoted && !doubleQuoted && !backQuote {
 | |
| 				if !dollarQuote && strings.HasSuffix(buf, "$") {
 | |
| 					dollarQuote = true
 | |
| 					buf += "("
 | |
| 					continue
 | |
| 				} else {
 | |
| 					return nil, errors.New("invalid command line string")
 | |
| 				}
 | |
| 			}
 | |
| 		case '"':
 | |
| 			if !singleQuoted && !dollarQuote {
 | |
| 				if doubleQuoted {
 | |
| 					got = argQuoted
 | |
| 				}
 | |
| 				doubleQuoted = !doubleQuoted
 | |
| 				continue
 | |
| 			}
 | |
| 		case '\'':
 | |
| 			if !doubleQuoted && !dollarQuote {
 | |
| 				if singleQuoted {
 | |
| 					got = argQuoted
 | |
| 				}
 | |
| 				singleQuoted = !singleQuoted
 | |
| 				continue
 | |
| 			}
 | |
| 		case ';', '&', '|', '<', '>':
 | |
| 			if !(escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote) {
 | |
| 				if r == '>' && len(buf) > 0 {
 | |
| 					if c := buf[0]; '0' <= c && c <= '9' {
 | |
| 						i -= 1
 | |
| 						got = argNo
 | |
| 					}
 | |
| 				}
 | |
| 				pos = i
 | |
| 				break loop
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		got = argSingle
 | |
| 		buf += string(r)
 | |
| 		if backQuote || dollarQuote {
 | |
| 			backtick += string(r)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if got != argNo {
 | |
| 		if p.ParseEnv {
 | |
| 			if got == argSingle {
 | |
| 				parser := &Parser{ParseEnv: false, ParseBacktick: false, Position: 0, Dir: p.Dir}
 | |
| 				strs, err := parser.Parse(replaceEnv(p.Getenv, buf))
 | |
| 				if err != nil {
 | |
| 					return nil, err
 | |
| 				}
 | |
| 				args = append(args, strs...)
 | |
| 			} else {
 | |
| 				args = append(args, replaceEnv(p.Getenv, buf))
 | |
| 			}
 | |
| 		} else {
 | |
| 			args = append(args, buf)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote {
 | |
| 		return nil, errors.New("invalid command line string")
 | |
| 	}
 | |
| 
 | |
| 	p.Position = pos
 | |
| 
 | |
| 	return args, nil
 | |
| }
 | |
| 
 | |
| func (p *Parser) ParseWithEnvs(line string) (envs []string, args []string, err error) {
 | |
| 	_args, err := p.Parse(line)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	envs = []string{}
 | |
| 	args = []string{}
 | |
| 	parsingEnv := true
 | |
| 	for _, arg := range _args {
 | |
| 		if parsingEnv && isEnv(arg) {
 | |
| 			envs = append(envs, arg)
 | |
| 		} else {
 | |
| 			if parsingEnv {
 | |
| 				parsingEnv = false
 | |
| 			}
 | |
| 			args = append(args, arg)
 | |
| 		}
 | |
| 	}
 | |
| 	return envs, args, nil
 | |
| }
 | |
| 
 | |
| func isEnv(arg string) bool {
 | |
| 	return len(strings.Split(arg, "=")) == 2
 | |
| }
 | |
| 
 | |
| func Parse(line string) ([]string, error) {
 | |
| 	return NewParser().Parse(line)
 | |
| }
 | |
| 
 | |
| func ParseWithEnvs(line string) (envs []string, args []string, err error) {
 | |
| 	return NewParser().ParseWithEnvs(line)
 | |
| }
 |