1
0
mirror of https://github.com/go-task/task.git synced 2024-12-16 10:59:23 +02:00
task/vendor/mvdan.cc/sh/interp/param.go

295 lines
6.3 KiB
Go
Raw Normal View History

2017-04-24 14:47:10 +02:00
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package interp
import (
2018-01-03 19:12:40 +02:00
"fmt"
"os"
"regexp"
2018-02-12 02:02:22 +02:00
"sort"
2017-04-24 14:47:10 +02:00
"strconv"
"strings"
"unicode"
"unicode/utf8"
"mvdan.cc/sh/syntax"
2017-04-24 14:47:10 +02:00
)
2018-01-03 19:12:40 +02:00
func anyOfLit(v interface{}, vals ...string) string {
word, _ := v.(*syntax.Word)
if word == nil || len(word.Parts) != 1 {
return ""
}
lit, ok := word.Parts[0].(*syntax.Lit)
if !ok {
return ""
}
for _, val := range vals {
if lit.Value == val {
return val
}
}
return ""
}
2018-02-12 02:02:22 +02:00
// quotedElems checks if a parameter expansion is exactly ${@} or ${foo[@]}
2017-06-04 21:06:04 +02:00
func (r *Runner) quotedElems(pe *syntax.ParamExp) []string {
2018-02-12 02:02:22 +02:00
if pe == nil || pe.Excl || pe.Length || pe.Width {
2017-06-04 21:06:04 +02:00
return nil
}
if pe.Param.Value == "@" {
2017-06-24 21:00:10 +02:00
return r.Params
2017-06-04 21:06:04 +02:00
}
2018-01-03 19:12:40 +02:00
if anyOfLit(pe.Index, "@") == "" {
2017-06-04 21:06:04 +02:00
return nil
}
val, _ := r.lookupVar(pe.Param.Value)
2018-06-24 15:40:44 +02:00
if x, ok := val.Value.(IndexArray); ok {
2017-06-04 21:06:04 +02:00
return x
}
return nil
}
2017-04-24 14:47:10 +02:00
func (r *Runner) paramExp(pe *syntax.ParamExp) string {
name := pe.Param.Value
2018-01-03 19:12:40 +02:00
var vr Variable
2017-04-24 14:47:10 +02:00
set := false
2018-01-03 19:12:40 +02:00
index := pe.Index
2017-04-24 14:47:10 +02:00
switch name {
case "#":
2018-01-03 19:12:40 +02:00
vr.Value = StringVal(strconv.Itoa(len(r.Params)))
case "@", "*":
vr.Value = IndexArray(r.Params)
index = &syntax.Word{Parts: []syntax.WordPart{
&syntax.Lit{Value: name},
}}
2017-04-24 14:47:10 +02:00
case "?":
2018-01-03 19:12:40 +02:00
vr.Value = StringVal(strconv.Itoa(r.exit))
case "$":
vr.Value = StringVal(strconv.Itoa(os.Getpid()))
case "PPID":
vr.Value = StringVal(strconv.Itoa(os.Getppid()))
case "LINENO":
line := uint64(pe.Pos().Line())
vr.Value = StringVal(strconv.FormatUint(line, 10))
2018-02-12 02:02:22 +02:00
case "DIRSTACK":
vr.Value = IndexArray(r.dirStack)
2017-04-24 14:47:10 +02:00
default:
if n, err := strconv.Atoi(name); err == nil {
2017-06-24 21:00:10 +02:00
if i := n - 1; i < len(r.Params) {
2018-01-03 19:12:40 +02:00
vr.Value, set = StringVal(r.Params[i]), true
2017-04-24 14:47:10 +02:00
}
} else {
2018-01-03 19:12:40 +02:00
vr, set = r.lookupVar(name)
2017-04-24 14:47:10 +02:00
}
}
2018-01-03 19:12:40 +02:00
str := r.varStr(vr, 0)
if index != nil {
str = r.varInd(vr, index, 0)
}
2018-01-21 13:39:15 +02:00
slicePos := func(expr syntax.ArithmExpr) int {
p := r.arithm(expr)
if p < 0 {
p = len(str) + p
if p < 0 {
p = len(str)
}
} else if p > len(str) {
p = len(str)
}
return p
}
2018-02-12 02:02:22 +02:00
elems := []string{str}
if anyOfLit(index, "@", "*") != "" {
switch x := vr.Value.(type) {
case nil:
elems = nil
case IndexArray:
elems = x
}
}
2018-01-21 13:39:15 +02:00
switch {
case pe.Length:
2018-02-12 02:02:22 +02:00
n := len(elems)
if anyOfLit(index, "@", "*") == "" {
2018-01-03 19:12:40 +02:00
n = utf8.RuneCountInString(str)
}
str = strconv.Itoa(n)
case pe.Excl:
2018-02-12 02:02:22 +02:00
var strs []string
2018-01-21 13:39:15 +02:00
if pe.Names != 0 {
2018-02-12 02:02:22 +02:00
strs = r.namesByPrefix(pe.Param.Value)
2018-01-21 13:39:15 +02:00
} else if vr.NameRef {
2018-02-12 02:02:22 +02:00
strs = append(strs, string(vr.Value.(StringVal)))
} else if x, ok := vr.Value.(IndexArray); ok {
for i, e := range x {
if e != "" {
strs = append(strs, strconv.Itoa(i))
}
}
} else if x, ok := vr.Value.(AssocArray); ok {
for k := range x {
strs = append(strs, k)
}
2018-01-21 13:39:15 +02:00
} else if str != "" {
vr, _ = r.lookupVar(str)
2018-02-12 02:02:22 +02:00
strs = append(strs, r.varStr(vr, 0))
2018-01-03 19:12:40 +02:00
}
2018-02-12 02:02:22 +02:00
sort.Strings(strs)
str = strings.Join(strs, " ")
2018-01-21 13:39:15 +02:00
case pe.Slice != nil:
2017-04-24 14:47:10 +02:00
if pe.Slice.Offset != nil {
offset := slicePos(pe.Slice.Offset)
str = str[offset:]
}
if pe.Slice.Length != nil {
length := slicePos(pe.Slice.Length)
str = str[:length]
}
2018-01-21 13:39:15 +02:00
case pe.Repl != nil:
2018-01-03 19:12:40 +02:00
orig := r.lonePattern(pe.Repl.Orig)
2017-04-24 14:47:10 +02:00
with := r.loneWord(pe.Repl.With)
n := 1
if pe.Repl.All {
n = -1
}
2018-01-03 19:12:40 +02:00
locs := findAllIndex(orig, str, n)
buf := r.strBuilder()
last := 0
for _, loc := range locs {
buf.WriteString(str[last:loc[0]])
buf.WriteString(with)
last = loc[1]
}
buf.WriteString(str[last:])
str = buf.String()
2018-01-21 13:39:15 +02:00
case pe.Exp != nil:
2017-04-24 14:47:10 +02:00
arg := r.loneWord(pe.Exp.Word)
2018-02-12 02:02:22 +02:00
switch op := pe.Exp.Op; op {
2017-04-24 14:47:10 +02:00
case syntax.SubstColPlus:
if str == "" {
break
}
fallthrough
case syntax.SubstPlus:
if set {
str = arg
}
case syntax.SubstMinus:
if set {
break
}
fallthrough
case syntax.SubstColMinus:
if str == "" {
str = arg
}
case syntax.SubstQuest:
if set {
break
}
fallthrough
case syntax.SubstColQuest:
if str == "" {
2018-01-03 19:12:40 +02:00
r.errf("%s\n", arg)
2017-04-24 14:47:10 +02:00
r.exit = 1
r.lastExit()
}
case syntax.SubstAssgn:
if set {
break
}
fallthrough
case syntax.SubstColAssgn:
if str == "" {
2018-01-03 19:12:40 +02:00
r.setVarString(name, arg)
2017-04-24 14:47:10 +02:00
str = arg
}
2018-02-12 02:02:22 +02:00
case syntax.RemSmallPrefix, syntax.RemLargePrefix,
syntax.RemSmallSuffix, syntax.RemLargeSuffix:
suffix := op == syntax.RemSmallSuffix ||
op == syntax.RemLargeSuffix
large := op == syntax.RemLargePrefix ||
op == syntax.RemLargeSuffix
for i, elem := range elems {
elems[i] = removePattern(elem, arg, suffix, large)
}
str = strings.Join(elems, " ")
case syntax.UpperFirst, syntax.UpperAll,
syntax.LowerFirst, syntax.LowerAll:
caseFunc := unicode.ToLower
if op == syntax.UpperFirst || op == syntax.UpperAll {
caseFunc = unicode.ToUpper
}
all := op == syntax.UpperAll || op == syntax.LowerAll
// empty string means '?'; nothing to do there
expr, err := syntax.TranslatePattern(arg, false)
if err != nil {
return str
}
rx := regexp.MustCompile(expr)
for i, elem := range elems {
rs := []rune(elem)
for ri, r := range rs {
if rx.MatchString(string(r)) {
rs[ri] = caseFunc(r)
if !all {
break
}
}
}
elems[i] = string(rs)
}
str = strings.Join(elems, " ")
2017-05-01 00:50:22 +02:00
case syntax.OtherParamOps:
2017-04-24 14:47:10 +02:00
switch arg {
case "Q":
str = strconv.Quote(str)
case "E":
tail := str
var rns []rune
for tail != "" {
var rn rune
rn, _, tail, _ = strconv.UnquoteChar(tail, 0)
rns = append(rns, rn)
}
str = string(rns)
case "P", "A", "a":
2018-01-03 19:12:40 +02:00
panic(fmt.Sprintf("unhandled @%s param expansion", arg))
2017-04-24 14:47:10 +02:00
default:
2018-01-03 19:12:40 +02:00
panic(fmt.Sprintf("unexpected @%s param expansion", arg))
2017-04-24 14:47:10 +02:00
}
}
}
return str
}
2018-01-03 19:12:40 +02:00
func removePattern(str, pattern string, fromEnd, greedy bool) string {
expr, err := syntax.TranslatePattern(pattern, greedy)
if err != nil {
return str
2017-04-24 14:47:10 +02:00
}
2018-01-03 19:12:40 +02:00
switch {
case fromEnd && !greedy:
// use .* to get the right-most (shortest) match
expr = ".*(" + expr + ")$"
case fromEnd:
// simple suffix
expr = "(" + expr + ")$"
default:
// simple prefix
expr = "^(" + expr + ")"
}
// no need to check error as TranslatePattern returns one
rx := regexp.MustCompile(expr)
if loc := rx.FindStringSubmatchIndex(str); loc != nil {
// remove the original pattern (the submatch)
str = str[:loc[2]] + str[loc[3]:]
}
return str
2017-04-24 14:47:10 +02:00
}