github.com/cilki/sh@v2.6.4+incompatible/interp/vars.go (about)

     1  // Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
     2  // See LICENSE for licensing information
     3  
     4  package interp
     5  
     6  import (
     7  	"os"
     8  	"runtime"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"mvdan.cc/sh/expand"
    13  	"mvdan.cc/sh/syntax"
    14  )
    15  
    16  type overlayEnviron struct {
    17  	parent expand.Environ
    18  	values map[string]expand.Variable
    19  }
    20  
    21  func (o overlayEnviron) Get(name string) expand.Variable {
    22  	if vr, ok := o.values[name]; ok {
    23  		return vr
    24  	}
    25  	return o.parent.Get(name)
    26  }
    27  
    28  func (o overlayEnviron) Set(name string, vr expand.Variable) {
    29  	o.values[name] = vr
    30  }
    31  
    32  func (o overlayEnviron) Each(f func(name string, vr expand.Variable) bool) {
    33  	o.parent.Each(f)
    34  	for name, vr := range o.values {
    35  		if !f(name, vr) {
    36  			return
    37  		}
    38  	}
    39  }
    40  
    41  func execEnv(env expand.Environ) []string {
    42  	list := make([]string, 0, 32)
    43  	env.Each(func(name string, vr expand.Variable) bool {
    44  		if vr.Exported {
    45  			list = append(list, name+"="+vr.String())
    46  		}
    47  		return true
    48  	})
    49  	return list
    50  }
    51  
    52  func (r *Runner) lookupVar(name string) expand.Variable {
    53  	if name == "" {
    54  		panic("variable name must not be empty")
    55  	}
    56  	var value interface{}
    57  	switch name {
    58  	case "#":
    59  		value = strconv.Itoa(len(r.Params))
    60  	case "@", "*":
    61  		value = r.Params
    62  	case "?":
    63  		value = strconv.Itoa(r.exit)
    64  	case "$":
    65  		value = strconv.Itoa(os.Getpid())
    66  	case "PPID":
    67  		value = strconv.Itoa(os.Getppid())
    68  	case "DIRSTACK":
    69  		value = r.dirStack
    70  	case "0":
    71  		if r.filename != "" {
    72  			value = r.filename
    73  		} else {
    74  			value = "gosh"
    75  		}
    76  	case "1", "2", "3", "4", "5", "6", "7", "8", "9":
    77  		i := int(name[0] - '1')
    78  		if i < len(r.Params) {
    79  			value = r.Params[i]
    80  		} else {
    81  			value = ""
    82  		}
    83  	}
    84  	if value != nil {
    85  		return expand.Variable{Value: value}
    86  	}
    87  	if value, e := r.cmdVars[name]; e {
    88  		return expand.Variable{Value: value}
    89  	}
    90  	if vr, e := r.funcVars[name]; e {
    91  		vr.Local = true
    92  		return vr
    93  	}
    94  	if vr, e := r.Vars[name]; e {
    95  		return vr
    96  	}
    97  	if vr := r.Env.Get(name); vr.IsSet() {
    98  		return vr
    99  	}
   100  	if runtime.GOOS == "windows" {
   101  		upper := strings.ToUpper(name)
   102  		if vr := r.Env.Get(upper); vr.IsSet() {
   103  			return vr
   104  		}
   105  	}
   106  	if r.opts[optNoUnset] {
   107  		r.errf("%s: unbound variable\n", name)
   108  		r.setErr(ShellExitStatus(1))
   109  	}
   110  	return expand.Variable{}
   111  }
   112  
   113  func (r *Runner) envGet(name string) string {
   114  	return r.lookupVar(name).String()
   115  }
   116  
   117  func (r *Runner) delVar(name string) {
   118  	vr := r.lookupVar(name)
   119  	if vr.ReadOnly {
   120  		r.errf("%s: readonly variable\n", name)
   121  		r.exit = 1
   122  		return
   123  	}
   124  	if vr.Local {
   125  		// don't overwrite a non-local var with the same name
   126  		r.funcVars[name] = expand.Variable{}
   127  	} else {
   128  		r.Vars[name] = expand.Variable{} // to not query r.Env
   129  	}
   130  }
   131  
   132  func (r *Runner) setVarString(name, value string) {
   133  	r.setVar(name, nil, expand.Variable{Value: value})
   134  }
   135  
   136  func (r *Runner) setVarInternal(name string, vr expand.Variable) {
   137  	if _, ok := vr.Value.(string); ok {
   138  		if r.opts[optAllExport] {
   139  			vr.Exported = true
   140  		}
   141  	} else {
   142  		vr.Exported = false
   143  	}
   144  	if vr.Local {
   145  		if r.funcVars == nil {
   146  			r.funcVars = make(map[string]expand.Variable)
   147  		}
   148  		r.funcVars[name] = vr
   149  	} else {
   150  		r.Vars[name] = vr
   151  	}
   152  }
   153  
   154  func (r *Runner) setVar(name string, index syntax.ArithmExpr, vr expand.Variable) {
   155  	cur := r.lookupVar(name)
   156  	if cur.ReadOnly {
   157  		r.errf("%s: readonly variable\n", name)
   158  		r.exit = 1
   159  		return
   160  	}
   161  	if name2, var2 := cur.Resolve(r.Env); name2 != "" {
   162  		name = name2
   163  		cur = var2
   164  		vr.NameRef = false
   165  		cur.NameRef = false
   166  	}
   167  	_, isIndexArray := cur.Value.([]string)
   168  	_, isAssocArray := cur.Value.(map[string]string)
   169  
   170  	if _, ok := vr.Value.(string); ok && index == nil {
   171  		// When assigning a string to an array, fall back to the
   172  		// zero value for the index.
   173  		if isIndexArray {
   174  			index = &syntax.Word{Parts: []syntax.WordPart{
   175  				&syntax.Lit{Value: "0"},
   176  			}}
   177  		} else if isAssocArray {
   178  			index = &syntax.Word{Parts: []syntax.WordPart{
   179  				&syntax.DblQuoted{},
   180  			}}
   181  		}
   182  	}
   183  	if index == nil {
   184  		r.setVarInternal(name, vr)
   185  		return
   186  	}
   187  
   188  	// from the syntax package, we know that value must be a string if index
   189  	// is non-nil; nested arrays are forbidden.
   190  	valStr := vr.Value.(string)
   191  
   192  	// if the existing variable is already an AssocArray, try our best
   193  	// to convert the key to a string
   194  	if isAssocArray {
   195  		amap := cur.Value.(map[string]string)
   196  		w, ok := index.(*syntax.Word)
   197  		if !ok {
   198  			return
   199  		}
   200  		k := r.literal(w)
   201  		amap[k] = valStr
   202  		cur.Value = amap
   203  		r.setVarInternal(name, cur)
   204  		return
   205  	}
   206  	var list []string
   207  	switch x := cur.Value.(type) {
   208  	case string:
   209  		list = append(list, x)
   210  	case []string:
   211  		list = x
   212  	case map[string]string: // done above
   213  	}
   214  	k := r.arithm(index)
   215  	for len(list) < k+1 {
   216  		list = append(list, "")
   217  	}
   218  	list[k] = valStr
   219  	cur.Value = list
   220  	r.setVarInternal(name, cur)
   221  }
   222  
   223  func (r *Runner) setFunc(name string, body *syntax.Stmt) {
   224  	if r.Funcs == nil {
   225  		r.Funcs = make(map[string]*syntax.Stmt, 4)
   226  	}
   227  	r.Funcs[name] = body
   228  }
   229  
   230  func stringIndex(index syntax.ArithmExpr) bool {
   231  	w, ok := index.(*syntax.Word)
   232  	if !ok || len(w.Parts) != 1 {
   233  		return false
   234  	}
   235  	switch w.Parts[0].(type) {
   236  	case *syntax.DblQuoted, *syntax.SglQuoted:
   237  		return true
   238  	}
   239  	return false
   240  }
   241  
   242  func (r *Runner) assignVal(as *syntax.Assign, valType string) interface{} {
   243  	prev := r.lookupVar(as.Name.Value)
   244  	if as.Naked {
   245  		return prev.Value
   246  	}
   247  	if as.Value != nil {
   248  		s := r.literal(as.Value)
   249  		if !as.Append || !prev.IsSet() {
   250  			return s
   251  		}
   252  		switch x := prev.Value.(type) {
   253  		case string:
   254  			return x + s
   255  		case []string:
   256  			if len(x) == 0 {
   257  				x = append(x, "")
   258  			}
   259  			x[0] += s
   260  			return x
   261  		case map[string]string:
   262  			// TODO
   263  		}
   264  		return s
   265  	}
   266  	if as.Array == nil {
   267  		// don't return nil, as that's an unset variable
   268  		return ""
   269  	}
   270  	elems := as.Array.Elems
   271  	if valType == "" {
   272  		if len(elems) == 0 || !stringIndex(elems[0].Index) {
   273  			valType = "-a" // indexed
   274  		} else {
   275  			valType = "-A" // associative
   276  		}
   277  	}
   278  	if valType == "-A" {
   279  		// associative array
   280  		amap := make(map[string]string, len(elems))
   281  		for _, elem := range elems {
   282  			k := r.literal(elem.Index.(*syntax.Word))
   283  			amap[k] = r.literal(elem.Value)
   284  		}
   285  		if !as.Append || !prev.IsSet() {
   286  			return amap
   287  		}
   288  		// TODO
   289  		return amap
   290  	}
   291  	// indexed array
   292  	maxIndex := len(elems) - 1
   293  	indexes := make([]int, len(elems))
   294  	for i, elem := range elems {
   295  		if elem.Index == nil {
   296  			indexes[i] = i
   297  			continue
   298  		}
   299  		k := r.arithm(elem.Index)
   300  		indexes[i] = k
   301  		if k > maxIndex {
   302  			maxIndex = k
   303  		}
   304  	}
   305  	strs := make([]string, maxIndex+1)
   306  	for i, elem := range elems {
   307  		strs[indexes[i]] = r.literal(elem.Value)
   308  	}
   309  	if !as.Append || !prev.IsSet() {
   310  		return strs
   311  	}
   312  	switch x := prev.Value.(type) {
   313  	case string:
   314  		return append([]string{x}, strs...)
   315  	case []string:
   316  		return append(x, strs...)
   317  	case map[string]string:
   318  		// TODO
   319  	}
   320  	return strs
   321  }