func (s SubVariable) Sub(scp *variables.Scope) (returnString string) { logex.Debug("Substituting variable") defer func() { logex.Debugf("Returned '%s'", returnString) }() v := scp.Get(s.VarName) switch s.SubType { case VarSubNormal: return v.Val case VarSubLength: // For the values ${#*} and ${#@} // the number of positional parameters is returned // We need to perform IFS splitting to figure this out return strconv.Itoa(len(v.Val)) } varExists := v.Set == true // CheckNull means that an empty string is treated as unset if s.CheckNull { varExists = varExists && v.Val != "" } switch s.SubType { case VarSubAssign: if varExists { return v.Val } scp.Set(s.VarName, s.SubVal) return s.SubVal case VarSubMinus: if varExists { return v.Val } return s.SubVal case VarSubPlus: if varExists { return "" } return s.SubVal case VarSubQuestion: if varExists { return v.Val } if s.SubVal != "" { ExitShellWithMessage(T.ExitFailure, s.SubVal) } ExitShellWithMessage(T.ExitFailure, s.VarName+": Parameter not set") case VarSubTrimRight, VarSubTrimRightMax, VarSubTrimLeft, VarSubTrimLeftMax: ExitShellWithMessage(T.ExitFailure, "Trim operations not implemented") } logex.Fatal("SubVariable.Sub unreached") return "" }
func LocalCmd(scp *variables.Scope, ioc *T.IOContainer, args []string) T.ExitStatus { // Local should also do assignments, split args on equal sign? already // expanded for _, a := range args { tmp := scp.Get(a) if tmp.Set { scp.Set(a, tmp.Val, variables.LocalScope) } else { scp.Set(a, "", variables.LocalScope) } } return T.ExitSuccess }
func (n NodeFor) Eval(scp *variables.Scope, ioc *T.IOContainer) T.ExitStatus { returnExit := T.ExitSuccess expandedArgs := make([]string, len(n.Args)) for i, arg := range n.Args { // This will need to be changed when IFS splitting is coded. // Append each split as a seperate item expandedArgs[i] = arg.Expand(scp) } for _, arg := range expandedArgs { scp.Set(n.LoopVar, arg) returnExit = n.Body.Eval(scp, ioc) } return returnExit }
func (n NodeCommand) Eval(scp *variables.Scope, ioc *T.IOContainer) T.ExitStatus { // A line with only assignments applies them to the Root Scope // We check this first to avoid unnecessary scope Push/Pop's if len(n.Args) == 0 { for k, v := range n.Assign { scp.Set(k, v.Expand(scp)) } return T.ExitSuccess } // Minimum of len(n.Args) after expansions, Likely // that it will be more after globbing though expandedArgs := []string{} for _, arg := range n.Args { expandedArgs = append(expandedArgs, arg.Expand(scp)) } // Order of precedence: // Relative command > Builtin > User Function > Other external command command := expandedArgs[0] builtinFunc, builtinFound := builtins.All[command] userFunc, userFuncFound := scp.Functions[command] if strings.ContainsRune(command, '/') || (!builtinFound && !userFuncFound) { scp.Push() defer scp.Pop() for k, v := range n.Assign { scp.Set(k, v.Expand(scp), variables.LocalScope) } return n.execExternal(scp, ioc, expandedArgs) } if builtinFound { return builtinFunc(scp, ioc, expandedArgs[1:]) } if userFuncFound { x := userFunc.(NodeFunction) return x.EvalFunc(scp, ioc, expandedArgs[1:]) } return T.ExitUnknownCommand }