func CdCmd(scp *variables.Scope, ioc *T.IOContainer, args []string) T.ExitStatus { // This does not conform to the posix spec. // Very simplified. Cd to first arg or attempt to cd to home dir cdTarget := "." if len(args) == 0 { homeDir := scp.Get("HOME") if homeDir.Val == "" { // Implementation defined behaviour. We try to grab // the homedir of the current user u, err := user.Current() if err == nil { cdTarget = u.HomeDir } } else { cdTarget = homeDir.Val } } else { cdTarget = args[0] } if err := scp.SetPwd(cdTarget); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err.Error()) return T.ExitFailure } return T.ExitSuccess }
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 (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 (s SubSubshell) Sub(scp *variables.Scope) (returnString string) { logex.Debug("Substituting shell") defer func() { logex.Debugf("Returned '%s'", returnString) }() if _, isNoop := s.N.(NodeNoop); isNoop { return "" } out := &bytes.Buffer{} // Not sure if we need to capture this exit code for the $? var. // Ignore it for now _ = s.N.Eval(scp.Copy(), &T.IOContainer{&bytes.Buffer{}, out, os.Stderr}) return strings.TrimRight(out.String(), "\n") }
func (n NodePipe) Eval(scp *variables.Scope, ioc *T.IOContainer) T.ExitStatus { lastPipeReader, pipeWriter := io.Pipe() scp = scp.Copy() cmd := n.Commands[0] go cmd.Eval(scp, &T.IOContainer{In: &bytes.Buffer{}, Out: pipeWriter, Err: ioc.Err}) for _, cmd = range n.Commands[1 : len(n.Commands)-1] { pipeReader, pipeWriter := io.Pipe() go cmd.Eval(scp, &T.IOContainer{In: lastPipeReader, Out: pipeWriter, Err: ioc.Err}) lastPipeReader = pipeReader } cmd = n.Commands[len(n.Commands)-1] if !n.Background { return cmd.Eval(scp, &T.IOContainer{In: lastPipeReader, Out: ioc.Out, Err: ioc.Err}) } go cmd.Eval(scp, &T.IOContainer{In: lastPipeReader, Out: ioc.Out, Err: ioc.Err}) return T.ExitSuccess }
func (n NodeCommand) execExternal(scp *variables.Scope, ioc *T.IOContainer, args []string) T.ExitStatus { // This is needed so that pipes will terminate if pw, isPipeWriter := ioc.Out.(*io.PipeWriter); isPipeWriter { defer func() { if err := pw.Close(); err != nil { panic(err) // XXX: Print error to stdout and continue? } }() } cmd := exec.Command(args[0], args[1:]...) cmd.Env = scp.Environ() cmd.Stdin = ioc.In cmd.Stderr = ioc.Err cmd.Stdout = ioc.Out err := cmd.Run() if err == nil { return T.ExitSuccess } return T.ExitFailure }
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 }
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 NodeFunction) EvalFunc(scp *variables.Scope, ioc *T.IOContainer, args []string) T.ExitStatus { scp.PushFunction(args) defer scp.Pop() return n.Body.Eval(scp, ioc) }