github.com/NeowayLabs/nash@v0.2.2-0.20200127205349-a227041ffd50/internal/sh/shell.go (about)

     1  package sh
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net"
     9  	"os"
    10  	"os/signal"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strconv"
    14  	"strings"
    15  	"sync"
    16  	"syscall"
    17  
    18  	"github.com/madlambda/nash/ast"
    19  	"github.com/madlambda/nash/errors"
    20  	"github.com/madlambda/nash/internal/sh/builtin"
    21  	"github.com/madlambda/nash/parser"
    22  	"github.com/madlambda/nash/sh"
    23  	"github.com/madlambda/nash/token"
    24  )
    25  
    26  const (
    27  	logNS     = "nashell.Shell"
    28  	defPrompt = "\033[31mλ>\033[0m "
    29  )
    30  
    31  type (
    32  	// Env is the environment map of lists
    33  	Env map[string]sh.Obj
    34  	Var Env
    35  	Fns map[string]sh.FnDef
    36  
    37  	StatusCode uint8
    38  
    39  	// Shell is the core data structure.
    40  	Shell struct {
    41  		name        string
    42  		debug       bool
    43  		interactive bool
    44  		abortOnErr  bool
    45  		logf        LogFn
    46  		nashdPath   string
    47  		isFn        bool
    48  		filename    string // current file being executed or imported
    49  
    50  		sigs        chan os.Signal
    51  		interrupted bool
    52  		looping     bool
    53  
    54  		stdin  io.Reader
    55  		stdout io.Writer
    56  		stderr io.Writer
    57  
    58  		env   Env
    59  		vars  Var
    60  		binds Fns
    61  
    62  		root   *ast.Tree
    63  		parent *Shell
    64  
    65  		repr string // string representation
    66  
    67  		nashpath string
    68  		nashroot string
    69  
    70  		*sync.Mutex
    71  	}
    72  
    73  	errIgnore struct {
    74  		*errors.NashError
    75  	}
    76  
    77  	errInterrupted struct {
    78  		*errors.NashError
    79  	}
    80  
    81  	errStopWalking struct {
    82  		*errors.NashError
    83  	}
    84  )
    85  
    86  const (
    87  	ESuccess    StatusCode = 0
    88  	ENotFound              = 127
    89  	ENotStarted            = 255
    90  )
    91  
    92  func newErrIgnore(format string, arg ...interface{}) error {
    93  	e := &errIgnore{
    94  		NashError: errors.NewError(format, arg...),
    95  	}
    96  
    97  	return e
    98  }
    99  
   100  func (e *errIgnore) Ignore() bool { return true }
   101  
   102  func newErrInterrupted(format string, arg ...interface{}) error {
   103  	return &errInterrupted{
   104  		NashError: errors.NewError(format, arg...),
   105  	}
   106  }
   107  
   108  func (e *errInterrupted) Interrupted() bool { return true }
   109  
   110  func newErrStopWalking() *errStopWalking {
   111  	return &errStopWalking{
   112  		NashError: errors.NewError("return"),
   113  	}
   114  }
   115  
   116  func (e *errStopWalking) StopWalking() bool { return true }
   117  
   118  func NewAbortShell(nashpath string, nashroot string) (*Shell, error) {
   119  	return newShell(nashpath, nashroot, true)
   120  }
   121  
   122  // NewShell creates a new shell object
   123  // nashpath will be used to search libraries and nashroot will be used to
   124  // search for the standard library shipped with the language.
   125  func NewShell(nashpath string, nashroot string) (*Shell, error) {
   126  	return newShell(nashpath, nashroot, false)
   127  }
   128  
   129  func newShell(nashpath string, nashroot string, abort bool) (*Shell, error) {
   130  	shell := &Shell{
   131  		name:        "parent scope",
   132  		interactive: false,
   133  		abortOnErr:  abort,
   134  		isFn:        false,
   135  		logf:        NewLog(logNS, false),
   136  		nashdPath:   nashdAutoDiscover(),
   137  		stdout:      os.Stdout,
   138  		stderr:      os.Stderr,
   139  		stdin:       os.Stdin,
   140  		env:         make(Env),
   141  		vars:        make(Var),
   142  		binds:       make(Fns),
   143  		Mutex:       &sync.Mutex{},
   144  		sigs:        make(chan os.Signal, 1),
   145  		filename:    "<interactive>",
   146  		nashpath:    nashpath,
   147  		nashroot:    nashroot,
   148  	}
   149  
   150  	err := shell.setup()
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	shell.setupSignals()
   156  	err = validateDirs(nashpath, nashroot)
   157  	if err != nil {
   158  		if shell.abortOnErr {
   159  			return nil, err
   160  		}
   161  
   162  		printerr := func(msg string) {
   163  			shell.Stderr().Write([]byte(msg + "\n"))
   164  		}
   165  		printerr(err.Error())
   166  		printerr("please check your NASHPATH and NASHROOT so they point to valid locations")
   167  	}
   168  
   169  	return shell, nil
   170  }
   171  
   172  // NewSubShell creates a nashell.Shell that inherits the parent shell stdin,
   173  // stdout, stderr and mutex lock.
   174  // Every variable and function lookup is done first in the subshell and then, if
   175  // not found, in the parent shell recursively.
   176  func NewSubShell(name string, parent *Shell) *Shell {
   177  	return &Shell{
   178  		name:      name,
   179  		isFn:      true,
   180  		parent:    parent,
   181  		logf:      NewLog(logNS, false),
   182  		nashdPath: nashdAutoDiscover(),
   183  		stdout:    parent.Stdout(),
   184  		stderr:    parent.Stderr(),
   185  		stdin:     parent.Stdin(),
   186  		env:       make(Env),
   187  		vars:      make(Var),
   188  		binds:     make(Fns),
   189  		Mutex:     parent.Mutex,
   190  		filename:  parent.filename,
   191  	}
   192  }
   193  
   194  func (shell *Shell) NashPath() string {
   195  	return shell.nashpath
   196  }
   197  
   198  // initEnv creates a new environment from old one
   199  func (shell *Shell) initEnv(processEnv []string) error {
   200  	largs := make([]sh.Obj, len(os.Args))
   201  
   202  	for i := 0; i < len(os.Args); i++ {
   203  		largs[i] = sh.NewStrObj(os.Args[i])
   204  	}
   205  
   206  	argv := sh.NewListObj(largs)
   207  
   208  	shell.Setenv("argv", argv)
   209  	shell.Newvar("argv", argv)
   210  
   211  	for _, penv := range processEnv {
   212  		var value sh.Obj
   213  		p := strings.Split(penv, "=")
   214  
   215  		if len(p) >= 2 {
   216  			// TODO(i4k): handle lists correctly in the future
   217  			// argv is not special, every list must be handled correctly
   218  			if p[0] == "argv" {
   219  				continue
   220  			}
   221  
   222  			value = sh.NewStrObj(strings.Join(p[1:], "="))
   223  
   224  			shell.Setenv(p[0], value)
   225  			shell.Newvar(p[0], value)
   226  		}
   227  	}
   228  
   229  	pidVal := sh.NewStrObj(strconv.Itoa(os.Getpid()))
   230  
   231  	shell.Setenv("PID", pidVal)
   232  	shell.Newvar("PID", pidVal)
   233  
   234  	if _, ok := shell.Getenv("SHELL"); !ok {
   235  		shellVal := sh.NewStrObj(nashdAutoDiscover())
   236  		shell.Setenv("SHELL", shellVal)
   237  		shell.Newvar("SHELL", shellVal)
   238  	}
   239  
   240  	cwd, err := os.Getwd()
   241  
   242  	if err != nil {
   243  		return err
   244  	}
   245  
   246  	cwdObj := sh.NewStrObj(cwd)
   247  	shell.Setenv("PWD", cwdObj)
   248  	shell.Newvar("PWD", cwdObj)
   249  
   250  	return nil
   251  }
   252  
   253  // Reset internal state
   254  func (shell *Shell) Reset() {
   255  	shell.vars = make(Var)
   256  	shell.env = make(Env)
   257  	shell.binds = make(Fns)
   258  }
   259  
   260  // SetDebug enable/disable debug in the shell
   261  func (shell *Shell) SetDebug(d bool) {
   262  	shell.debug = d
   263  	shell.logf = NewLog(logNS, d)
   264  }
   265  
   266  // SetInteractive enable/disable shell interactive mode
   267  func (shell *Shell) SetInteractive(i bool) {
   268  	shell.interactive = i
   269  
   270  	if i {
   271  		_ = shell.setupDefaultBindings()
   272  	}
   273  }
   274  
   275  func (shell *Shell) Interactive() bool {
   276  	if shell.parent != nil {
   277  		return shell.parent.Interactive()
   278  	}
   279  
   280  	return shell.interactive
   281  }
   282  
   283  func (shell *Shell) SetName(a string) {
   284  	shell.name = a
   285  }
   286  
   287  func (shell *Shell) Name() string { return shell.name }
   288  
   289  func (shell *Shell) SetParent(a *Shell) {
   290  	shell.parent = a
   291  }
   292  
   293  func (shell *Shell) Environ() Env {
   294  	if shell.parent != nil {
   295  		return shell.parent.Environ()
   296  	}
   297  
   298  	return shell.env
   299  }
   300  
   301  func (shell *Shell) Getenv(name string) (sh.Obj, bool) {
   302  	if shell.parent != nil {
   303  		return shell.parent.Getenv(name)
   304  	}
   305  
   306  	value, ok := shell.env[name]
   307  	return value, ok
   308  }
   309  
   310  func (shell *Shell) Setenv(name string, value sh.Obj) {
   311  	if shell.parent != nil {
   312  		shell.parent.Setenv(name, value)
   313  		return
   314  	}
   315  
   316  	shell.Newvar(name, value)
   317  
   318  	shell.env[name] = value
   319  	os.Setenv(name, value.String())
   320  }
   321  
   322  func (shell *Shell) SetEnviron(processEnv []string) {
   323  	shell.env = make(Env)
   324  
   325  	for _, penv := range processEnv {
   326  		var value sh.Obj
   327  		p := strings.Split(penv, "=")
   328  
   329  		if len(p) == 2 {
   330  			value = sh.NewStrObj(p[1])
   331  
   332  			shell.Setenv(p[0], value)
   333  			shell.Newvar(p[0], value)
   334  		}
   335  	}
   336  }
   337  
   338  // GetLocalvar returns a local scoped variable.
   339  func (shell *Shell) GetLocalvar(name string) (sh.Obj, bool) {
   340  	v, ok := shell.vars[name]
   341  	return v, ok
   342  }
   343  
   344  // Getvar returns the value of the variable name. It could look in their
   345  // parent scopes if not found locally.
   346  func (shell *Shell) Getvar(name string) (sh.Obj, bool) {
   347  	if value, ok := shell.vars[name]; ok {
   348  		return value, ok
   349  	}
   350  
   351  	if shell.parent != nil {
   352  		return shell.parent.Getvar(name)
   353  	}
   354  
   355  	return nil, false
   356  }
   357  
   358  // GetFn returns the function name or error if not found.
   359  func (shell *Shell) GetFn(name string) (*sh.FnObj, error) {
   360  	shell.logf("Looking for function '%s' on shell '%s'\n", name, shell.name)
   361  	if obj, ok := shell.vars[name]; ok {
   362  		if obj.Type() == sh.FnType {
   363  			fnObj := obj.(*sh.FnObj)
   364  			return fnObj, nil
   365  		}
   366  		return nil, errors.NewError("Identifier '%s' is not a function", name)
   367  	}
   368  
   369  	if shell.parent != nil {
   370  		return shell.parent.GetFn(name)
   371  	}
   372  
   373  	return nil, fmt.Errorf("function '%s' not found", name)
   374  }
   375  
   376  func (shell *Shell) Setbindfn(name string, value sh.FnDef) {
   377  	shell.binds[name] = value
   378  }
   379  
   380  func (shell *Shell) Getbindfn(cmdName string) (sh.FnDef, bool) {
   381  	if fn, ok := shell.binds[cmdName]; ok {
   382  		return fn, true
   383  	}
   384  
   385  	if shell.parent != nil {
   386  		return shell.parent.Getbindfn(cmdName)
   387  	}
   388  
   389  	return nil, false
   390  }
   391  
   392  // Newvar creates a new variable in the scope.
   393  func (shell *Shell) Newvar(name string, value sh.Obj) {
   394  	shell.vars[name] = value
   395  }
   396  
   397  // Setvar updates the value of an existing variable. If the variable
   398  // doesn't exist in current scope it looks up in their parent scopes.
   399  // It returns true if the variable was found and updated.
   400  func (shell *Shell) Setvar(name string, value sh.Obj) bool {
   401  	_, ok := shell.vars[name]
   402  	if ok {
   403  		shell.vars[name] = value
   404  		return true
   405  	}
   406  
   407  	if shell.parent != nil {
   408  		return shell.parent.Setvar(name, value)
   409  	}
   410  
   411  	return false
   412  }
   413  
   414  func (shell *Shell) IsFn() bool     { return shell.isFn }
   415  func (shell *Shell) SetIsFn(b bool) { shell.isFn = b }
   416  
   417  // SetNashdPath sets an alternativa path to nashd
   418  func (shell *Shell) SetNashdPath(path string) {
   419  	shell.nashdPath = path
   420  }
   421  
   422  // SetStdin sets the stdin for commands
   423  func (shell *Shell) SetStdin(in io.Reader) {
   424  	shell.stdin = in
   425  }
   426  
   427  // SetStdout sets stdout for commands
   428  func (shell *Shell) SetStdout(out io.Writer) {
   429  	shell.stdout = out
   430  }
   431  
   432  // SetStderr sets stderr for commands
   433  func (shell *Shell) SetStderr(err io.Writer) {
   434  	shell.stderr = err
   435  }
   436  
   437  func (shell *Shell) Stdout() io.Writer { return shell.stdout }
   438  func (shell *Shell) Stderr() io.Writer { return shell.stderr }
   439  func (shell *Shell) Stdin() io.Reader  { return shell.stdin }
   440  
   441  // SetTree sets the internal tree of the interpreter. It's used for
   442  // sub-shells like `fn`.
   443  func (shell *Shell) SetTree(t *ast.Tree) {
   444  	shell.root = t
   445  }
   446  
   447  // Tree returns the internal tree of the subshell.
   448  func (shell *Shell) Tree() *ast.Tree { return shell.root }
   449  
   450  // SetRepr set the string representation of the shell
   451  func (shell *Shell) SetRepr(a string) {
   452  	shell.repr = a
   453  }
   454  
   455  func (shell *Shell) setupBuiltin() {
   456  	for name, constructor := range builtin.Constructors() {
   457  		fnDef := newBuiltinFnDef(name, shell, constructor)
   458  		shell.Newvar(name, sh.NewFnObj(fnDef))
   459  	}
   460  }
   461  
   462  func (shell *Shell) setupDefaultBindings() error {
   463  	// only one builtin fn... no need for advanced machinery yet
   464  	homeEnvVar := "HOME"
   465  	if runtime.GOOS == "windows" {
   466  		homeEnvVar = "HOMEPATH"
   467  	}
   468  	err := shell.Exec(shell.name, fmt.Sprintf(`fn nash_builtin_cd(args...) {
   469  	    var path = ""
   470  	    var l <= len($args)
   471              if $l == "0" {
   472                      path = $%s
   473              } else {
   474                      path = $args[0]
   475  	    }
   476  
   477              chdir($path)
   478          }
   479  
   480          bindfn nash_builtin_cd cd`, homeEnvVar))
   481  
   482  	return err
   483  }
   484  
   485  func (shell *Shell) setup() error {
   486  	err := shell.initEnv(os.Environ())
   487  	if err != nil {
   488  		return err
   489  	}
   490  
   491  	if shell.env["PROMPT"] == nil {
   492  		pobj := sh.NewStrObj(defPrompt)
   493  		shell.Setenv("PROMPT", pobj)
   494  		shell.Newvar("PROMPT", pobj)
   495  	}
   496  
   497  	_, ok := shell.Getvar("_")
   498  	if !ok {
   499  		shell.Newvar("_", sh.NewStrObj(""))
   500  	}
   501  
   502  	shell.setupBuiltin()
   503  	return err
   504  }
   505  
   506  func (shell *Shell) setupSignals() {
   507  	signal.Notify(shell.sigs, syscall.SIGINT)
   508  
   509  	go func() {
   510  		for {
   511  			sig := <-shell.sigs
   512  
   513  			switch sig {
   514  			case syscall.SIGINT:
   515  				shell.Lock()
   516  
   517  				// TODO(i4k): Review implementation when interrupted inside
   518  				// function loops
   519  				if shell.looping {
   520  					shell.setIntr(true)
   521  				}
   522  
   523  				shell.Unlock()
   524  			default:
   525  				fmt.Printf("%s\n", sig)
   526  			}
   527  		}
   528  	}()
   529  }
   530  
   531  // TriggerCTRLC mock the user pressing CTRL-C in the terminal
   532  func (shell *Shell) TriggerCTRLC() error {
   533  	p, err := os.FindProcess(os.Getpid())
   534  	if err != nil {
   535  		return err
   536  	}
   537  
   538  	return p.Signal(syscall.SIGINT)
   539  }
   540  
   541  // setIntr *do not lock*. You must do it yourself!
   542  func (shell *Shell) setIntr(b bool) {
   543  	if shell.parent != nil {
   544  		shell.parent.setIntr(b)
   545  		return
   546  	}
   547  
   548  	shell.interrupted = b
   549  }
   550  
   551  // getIntr returns true if nash was interrupted by CTRL-C
   552  func (shell *Shell) getIntr() bool {
   553  	if shell.parent != nil {
   554  		return shell.parent.getIntr()
   555  	}
   556  
   557  	return shell.interrupted
   558  }
   559  
   560  // Exec executes the commands specified by string content
   561  func (shell *Shell) Exec(path, content string) error {
   562  	p := parser.NewParser(path, content)
   563  
   564  	tr, err := p.Parse()
   565  
   566  	if err != nil {
   567  		return err
   568  	}
   569  
   570  	_, err = shell.ExecuteTree(tr)
   571  	return err
   572  }
   573  
   574  // Execute the nash file at given path
   575  func (shell *Shell) ExecFile(path string) error {
   576  	bkCurFile := shell.filename
   577  
   578  	content, err := ioutil.ReadFile(path)
   579  
   580  	if err != nil {
   581  		return err
   582  	}
   583  
   584  	shell.filename = path
   585  
   586  	defer func() {
   587  		shell.filename = bkCurFile
   588  	}()
   589  
   590  	return shell.Exec(path, string(content))
   591  }
   592  
   593  func (shell *Shell) newvar(name *ast.NameNode, value sh.Obj) error {
   594  	if name.Index == nil {
   595  		shell.Newvar(name.Ident, value)
   596  		return nil
   597  	}
   598  
   599  	// handles ident[x] = v
   600  
   601  	obj, ok := shell.Getvar(name.Ident)
   602  	if !ok {
   603  		return errors.NewEvalError(shell.filename,
   604  			name, "Variable %s not found", name.Ident)
   605  	}
   606  
   607  	index, err := shell.evalIndex(name.Index)
   608  	if err != nil {
   609  		return err
   610  	}
   611  
   612  	col, err := sh.NewWriteableCollection(obj)
   613  	if err != nil {
   614  		return errors.NewEvalError(shell.filename, name, err.Error())
   615  	}
   616  
   617  	err = col.Set(index, value)
   618  	if err != nil {
   619  		return errors.NewEvalError(
   620  			shell.filename,
   621  			name,
   622  			"error[%s] setting var",
   623  			err,
   624  		)
   625  	}
   626  
   627  	shell.Newvar(name.Ident, obj)
   628  	return nil
   629  }
   630  
   631  func (shell *Shell) setvar(name *ast.NameNode, value sh.Obj) error {
   632  	if name.Index == nil {
   633  		if !shell.Setvar(name.Ident, value) {
   634  			return errors.NewEvalError(shell.filename,
   635  				name, "Variable '%s' is not initialized. Use 'var %s = <value>'",
   636  				name, name)
   637  		}
   638  		return nil
   639  	}
   640  
   641  	obj, ok := shell.Getvar(name.Ident)
   642  	if !ok {
   643  		return errors.NewEvalError(shell.filename,
   644  			name, "Variable %s not found", name.Ident)
   645  	}
   646  
   647  	index, err := shell.evalIndex(name.Index)
   648  	if err != nil {
   649  		return err
   650  	}
   651  
   652  	col, err := sh.NewWriteableCollection(obj)
   653  	if err != nil {
   654  		return errors.NewEvalError(shell.filename, name, err.Error())
   655  	}
   656  
   657  	err = col.Set(index, value)
   658  	if err != nil {
   659  		return errors.NewEvalError(
   660  			shell.filename,
   661  			name,
   662  			"error[%s] setting var",
   663  			err,
   664  		)
   665  	}
   666  
   667  	if !shell.Setvar(name.Ident, obj) {
   668  		return errors.NewEvalError(shell.filename,
   669  			name, "Variable '%s' is not initialized. Use 'var %s = <value>'",
   670  			name, name)
   671  	}
   672  	return nil
   673  }
   674  
   675  func (shell *Shell) setvars(names []*ast.NameNode, values []sh.Obj) error {
   676  	for i := 0; i < len(names); i++ {
   677  		err := shell.setvar(names[i], values[i])
   678  		if err != nil {
   679  			return err
   680  		}
   681  	}
   682  	return nil
   683  }
   684  
   685  func (shell *Shell) newvars(names []*ast.NameNode, values []sh.Obj) {
   686  	for i := 0; i < len(names); i++ {
   687  		shell.newvar(names[i], values[i])
   688  	}
   689  }
   690  
   691  func (shell *Shell) setcmdvars(names []*ast.NameNode, stdout, stderr, status sh.Obj) error {
   692  	if len(names) == 3 {
   693  		err := shell.setvar(names[0], stdout)
   694  		if err != nil {
   695  			return err
   696  		}
   697  		err = shell.setvar(names[1], stderr)
   698  		if err != nil {
   699  			return err
   700  		}
   701  		return shell.setvar(names[2], status)
   702  	} else if len(names) == 2 {
   703  		err := shell.setvar(names[0], stdout)
   704  		if err != nil {
   705  			return err
   706  		}
   707  		return shell.setvar(names[1], status)
   708  	} else if len(names) == 1 {
   709  		return shell.setvar(names[0], stdout)
   710  	}
   711  
   712  	panic(fmt.Sprintf("internal error: expects 1 <= len(names) <= 3,"+
   713  		" but got %d",
   714  		len(names)))
   715  
   716  	return nil
   717  }
   718  
   719  func (shell *Shell) newcmdvars(names []*ast.NameNode, stdout, stderr, status sh.Obj) {
   720  	if len(names) == 3 {
   721  		shell.newvar(names[0], stdout)
   722  		shell.newvar(names[1], stderr)
   723  		shell.newvar(names[2], status)
   724  	} else if len(names) == 2 {
   725  		shell.newvar(names[0], stdout)
   726  		shell.newvar(names[1], status)
   727  	} else if len(names) == 1 {
   728  		shell.newvar(names[0], stdout)
   729  	} else {
   730  		panic(fmt.Sprintf("internal error: expects 1 <= len(names) <= 3,"+
   731  			" but got %d",
   732  			len(names)))
   733  	}
   734  }
   735  
   736  // evalConcat reveives the AST representation of a concatenation of objects and
   737  // returns the string representation, or error.
   738  func (shell *Shell) evalConcat(path ast.Expr) (string, error) {
   739  	var pathStr string
   740  
   741  	if path.Type() != ast.NodeConcatExpr {
   742  		return "", fmt.Errorf("Invalid node %+v", path)
   743  	}
   744  
   745  	concatExpr := path.(*ast.ConcatExpr)
   746  	concat := concatExpr.List()
   747  
   748  	for i := 0; i < len(concat); i++ {
   749  		part := concat[i]
   750  
   751  		switch part.Type() {
   752  		case ast.NodeConcatExpr:
   753  			return "", errors.NewEvalError(shell.filename, part,
   754  				"Nested concat is not allowed: %s", part)
   755  		case ast.NodeVarExpr, ast.NodeIndexExpr:
   756  			partValue, err := shell.evalVariable(part)
   757  			if err != nil {
   758  				return "", err
   759  			}
   760  
   761  			if partValue.Type() == sh.ListType {
   762  				return "", errors.NewEvalError(shell.filename,
   763  					part, "Concat of list variables is not allowed: %v = %v",
   764  					part, partValue)
   765  			} else if partValue.Type() != sh.StringType {
   766  				return "", errors.NewEvalError(shell.filename, part,
   767  					"Invalid concat element: %v", partValue)
   768  			}
   769  
   770  			strval := partValue.(*sh.StrObj)
   771  			pathStr += strval.Str()
   772  		case ast.NodeStringExpr:
   773  			str, ok := part.(*ast.StringExpr)
   774  			if !ok {
   775  				return "", errors.NewEvalError(shell.filename, part,
   776  					"Failed to eval string: %s", part)
   777  			}
   778  
   779  			pathStr += str.Value()
   780  		case ast.NodeFnInv:
   781  			fnNode := part.(*ast.FnInvNode)
   782  			result, err := shell.executeFnInv(fnNode)
   783  			if err != nil {
   784  				return "", err
   785  			}
   786  
   787  			if len(result) == 0 || len(result) > 1 {
   788  				return "", errors.NewEvalError(shell.filename, part,
   789  					"Function '%s' used in string concat but returns %d values.",
   790  					fnNode.Name)
   791  			}
   792  			obj := result[0]
   793  			if obj.Type() != sh.StringType {
   794  				return "", errors.NewEvalError(shell.filename, part,
   795  					"Function '%s' used in concat but returns a '%s'", obj.Type())
   796  			}
   797  
   798  			str := obj.(*sh.StrObj)
   799  			pathStr += str.Str()
   800  		case ast.NodeListExpr:
   801  			return "", errors.NewEvalError(shell.filename, part,
   802  				"Concat of lists is not allowed: %+v", part.String())
   803  		default:
   804  			return "", errors.NewEvalError(shell.filename, part,
   805  				"Invalid argument: %+v", part)
   806  		}
   807  	}
   808  
   809  	return pathStr, nil
   810  }
   811  
   812  func (shell *Shell) executeNode(node ast.Node) ([]sh.Obj, error) {
   813  	var (
   814  		objs []sh.Obj
   815  		err  error
   816  	)
   817  
   818  	shell.logf("Executing node: %v\n", node)
   819  
   820  	switch node.Type() {
   821  	case ast.NodeImport:
   822  		err = shell.executeImport(node.(*ast.ImportNode))
   823  	case ast.NodeComment:
   824  		// ignore
   825  	case ast.NodeSetenv:
   826  		err = shell.executeSetenv(node.(*ast.SetenvNode))
   827  	case ast.NodeVarAssignDecl:
   828  		err = shell.executeVarAssign(node.(*ast.VarAssignDeclNode))
   829  	case ast.NodeVarExecAssignDecl:
   830  		err = shell.executeVarExecAssign(node.(*ast.VarExecAssignDeclNode))
   831  	case ast.NodeAssign:
   832  		err = shell.executeAssignment(node.(*ast.AssignNode))
   833  	case ast.NodeExecAssign:
   834  		err = shell.executeExecAssign(node.(*ast.ExecAssignNode))
   835  	case ast.NodeCommand:
   836  		_, err = shell.executeCommand(node.(*ast.CommandNode))
   837  	case ast.NodePipe:
   838  		_, err = shell.executePipe(node.(*ast.PipeNode))
   839  	case ast.NodeRfork:
   840  		err = shell.executeRfork(node.(*ast.RforkNode))
   841  	case ast.NodeIf:
   842  		objs, err = shell.executeIf(node.(*ast.IfNode))
   843  	case ast.NodeFnDecl:
   844  		err = shell.executeFnDecl(node.(*ast.FnDeclNode))
   845  	case ast.NodeFnInv:
   846  		// invocation ignoring output
   847  		_, err = shell.executeFnInv(node.(*ast.FnInvNode))
   848  	case ast.NodeFor:
   849  		objs, err = shell.executeFor(node.(*ast.ForNode))
   850  	case ast.NodeBindFn:
   851  		err = shell.executeBindFn(node.(*ast.BindFnNode))
   852  	case ast.NodeReturn:
   853  		if shell.IsFn() {
   854  			objs, err = shell.executeReturn(node.(*ast.ReturnNode))
   855  		} else {
   856  			err = errors.NewEvalError(shell.filename,
   857  				node,
   858  				"Unexpected return outside of function declaration.")
   859  		}
   860  	default:
   861  		// should never get here
   862  		return nil, errors.NewEvalError(shell.filename, node,
   863  			"invalid node: %v.", node.Type())
   864  	}
   865  
   866  	return objs, err
   867  }
   868  
   869  func (shell *Shell) ExecuteTree(tr *ast.Tree) ([]sh.Obj, error) {
   870  	return shell.executeTree(tr, true)
   871  }
   872  
   873  // executeTree evaluates the given tree
   874  func (shell *Shell) executeTree(tr *ast.Tree, stopable bool) ([]sh.Obj, error) {
   875  	if tr == nil || tr.Root == nil {
   876  		return nil, errors.NewError("empty abstract syntax tree to execute")
   877  	}
   878  
   879  	root := tr.Root
   880  
   881  	for _, node := range root.Nodes {
   882  		objs, err := shell.executeNode(node)
   883  		if err != nil {
   884  			type (
   885  				IgnoreError interface {
   886  					Ignore() bool
   887  				}
   888  
   889  				InterruptedError interface {
   890  					Interrupted() bool
   891  				}
   892  
   893  				StopWalkingError interface {
   894  					StopWalking() bool
   895  				}
   896  			)
   897  
   898  			if errIgnore, ok := err.(IgnoreError); ok && errIgnore.Ignore() {
   899  				continue
   900  			}
   901  
   902  			if errInterrupted, ok := err.(InterruptedError); ok && errInterrupted.Interrupted() {
   903  				return nil, err
   904  			}
   905  
   906  			if errStopWalking, ok := err.(StopWalkingError); stopable && ok && errStopWalking.StopWalking() {
   907  				return objs, nil
   908  			}
   909  
   910  			return objs, err
   911  		}
   912  	}
   913  
   914  	return nil, nil
   915  }
   916  
   917  func (shell *Shell) executeReturn(n *ast.ReturnNode) ([]sh.Obj, error) {
   918  	var returns []sh.Obj
   919  
   920  	returnExprs := n.Returns
   921  
   922  	for i := 0; i < len(returnExprs); i++ {
   923  		retExpr := returnExprs[i]
   924  
   925  		obj, err := shell.evalExpr(retExpr)
   926  		if err != nil {
   927  			return nil, err
   928  		}
   929  
   930  		returns = append(returns, obj)
   931  	}
   932  
   933  	return returns, newErrStopWalking()
   934  }
   935  
   936  func (shell *Shell) getNashRootFromGOPATH(preverr error) (string, error) {
   937  	g, hasgopath := shell.Getenv("GOPATH")
   938  	if !hasgopath {
   939  		return "", errors.NewError("%s\nno GOPATH env var setted", preverr)
   940  	}
   941  	gopath := g.String()
   942  	return filepath.Join(gopath, filepath.FromSlash("/src/github.com/madlambda/nash")), nil
   943  }
   944  
   945  func isValidNashRoot(nashroot string) bool {
   946  	_, err := os.Stat(filepath.Join(nashroot, "stdlib"))
   947  	return err == nil
   948  }
   949  
   950  func (shell *Shell) executeImport(node *ast.ImportNode) error {
   951  	obj, err := shell.evalExpr(node.Path)
   952  	if err != nil {
   953  		return errors.NewEvalError(shell.filename,
   954  			node, err.Error())
   955  	}
   956  
   957  	if obj.Type() != sh.StringType {
   958  		return errors.NewEvalError(shell.filename,
   959  			node.Path,
   960  			"Invalid type on import argument: %s", obj.Type())
   961  	}
   962  
   963  	objstr := obj.(*sh.StrObj)
   964  	fname := objstr.Str()
   965  
   966  	shell.logf("Importing '%s'", fname)
   967  
   968  	var (
   969  		tries  []string
   970  		hasExt bool
   971  	)
   972  
   973  	hasExt = filepath.Ext(fname) != ""
   974  	if filepath.IsAbs(fname) {
   975  		tries = append(tries, fname)
   976  
   977  		if !hasExt {
   978  			tries = append(tries, fname+".sh")
   979  		}
   980  	}
   981  
   982  	if shell.filename != "" {
   983  		localFile := filepath.Join(filepath.Dir(shell.filename), fname)
   984  		tries = append(tries, localFile)
   985  
   986  		if !hasExt {
   987  			tries = append(tries, localFile+".sh")
   988  		}
   989  	}
   990  
   991  	tries = append(tries, filepath.Join(shell.nashpath, "lib", fname))
   992  	if !hasExt {
   993  		tries = append(tries, filepath.Join(shell.nashpath, "lib", fname+".sh"))
   994  	}
   995  
   996  	tries = append(tries, filepath.Join(shell.nashroot, "stdlib", fname+".sh"))
   997  
   998  	shell.logf("Trying %q\n", tries)
   999  
  1000  	for _, path := range tries {
  1001  		d, err := os.Stat(path)
  1002  
  1003  		if err != nil {
  1004  			continue
  1005  		}
  1006  
  1007  		if m := d.Mode(); !m.IsDir() {
  1008  			return shell.ExecFile(path)
  1009  		}
  1010  	}
  1011  
  1012  	errmsg := fmt.Sprintf(
  1013  		"Failed to import path '%s'. The locations below have been tried:\n \"%s\"",
  1014  		fname,
  1015  		strings.Join(tries, `", "`),
  1016  	)
  1017  
  1018  	return errors.NewEvalError(shell.filename, node, errmsg)
  1019  }
  1020  
  1021  // executePipe executes a pipe of ast.Command's. Each command can be
  1022  // a path command in the operating system or a function bind to a
  1023  // command name.
  1024  // The error of each command can be suppressed prepending it with '-' (dash).
  1025  // The error returned will be a string representing the errors (or none) of
  1026  // each command separated by '|'. The $status of pipe execution will be
  1027  // the $status of each command separated by '|'.
  1028  func (shell *Shell) executePipe(pipe *ast.PipeNode) (sh.Obj, error) {
  1029  	var (
  1030  		closeFiles     []io.Closer
  1031  		closeAfterWait []io.Closer
  1032  		errIndex       int
  1033  		err            error
  1034  	)
  1035  
  1036  	defer func() {
  1037  		for _, c := range closeAfterWait {
  1038  			c.Close()
  1039  		}
  1040  	}()
  1041  
  1042  	nodeCommands := pipe.Commands()
  1043  
  1044  	if len(nodeCommands) < 2 {
  1045  		return sh.NewStrObj(strconv.Itoa(ENotStarted)),
  1046  			errors.NewEvalError(shell.filename,
  1047  				pipe, "Pipe requires at least two commands.")
  1048  	}
  1049  
  1050  	cmds := make([]sh.Runner, len(nodeCommands))
  1051  	errs := make([]string, len(nodeCommands))
  1052  	igns := make([]bool, len(nodeCommands)) // ignoreErrors
  1053  	cods := make([]string, len(nodeCommands))
  1054  
  1055  	for i := 0; i < len(nodeCommands); i++ {
  1056  		errs[i] = "not started"
  1057  		cods[i] = strconv.Itoa(ENotStarted)
  1058  	}
  1059  
  1060  	last := len(nodeCommands) - 1
  1061  
  1062  	envVars := buildenv(shell.Environ())
  1063  
  1064  	// Create all commands
  1065  	for i := 0; i < len(nodeCommands); i++ {
  1066  		var (
  1067  			cmd    sh.Runner
  1068  			ignore bool
  1069  			args   []sh.Obj
  1070  		)
  1071  
  1072  		nodeCmd := nodeCommands[i]
  1073  
  1074  		cmd, ignore, err = shell.getCommand(nodeCmd)
  1075  
  1076  		igns[i] = ignore
  1077  
  1078  		if err != nil {
  1079  			errIndex = i
  1080  			cods[i] = strconv.Itoa(ENotFound)
  1081  			goto pipeError
  1082  		}
  1083  
  1084  		// SetEnviron must be called before SetArgs
  1085  		// otherwise the subshell will have the arguments
  1086  		// shadowed by parent env
  1087  		cmd.SetEnviron(envVars)
  1088  		args, err = shell.evalExprs(nodeCmd.Args())
  1089  
  1090  		if err != nil {
  1091  			errIndex = i
  1092  			goto pipeError
  1093  		}
  1094  
  1095  		err = cmd.SetArgs(args)
  1096  		if err != nil {
  1097  			errIndex = i
  1098  			goto pipeError
  1099  		}
  1100  
  1101  		cmd.SetStdin(shell.stdin)
  1102  
  1103  		if i < last {
  1104  			closeFiles, err = shell.setRedirects(cmd, nodeCmd.Redirects())
  1105  			closeAfterWait = append(closeAfterWait, closeFiles...)
  1106  
  1107  			if err != nil {
  1108  				errIndex = i
  1109  				goto pipeError
  1110  			}
  1111  		}
  1112  
  1113  		cmds[i] = cmd
  1114  	}
  1115  
  1116  	// Shell does not support stdin redirection yet
  1117  	cmds[0].SetStdin(shell.stdin)
  1118  
  1119  	// Setup the commands. Pointing the stdin of next command to stdout of previous.
  1120  	// Except the stdout of last one
  1121  	for i, cmd := range cmds[:last] {
  1122  		var (
  1123  			stdin io.ReadCloser
  1124  		)
  1125  
  1126  		// StdoutPipe complains if Stdout is already set
  1127  		cmd.SetStdout(nil)
  1128  		stdin, err = cmd.StdoutPipe()
  1129  
  1130  		if err != nil {
  1131  			errIndex = i
  1132  			goto pipeError
  1133  		}
  1134  
  1135  		cmds[i+1].SetStdin(stdin)
  1136  	}
  1137  
  1138  	cmds[last].SetStdout(shell.stdout)
  1139  	cmds[last].SetStderr(shell.stderr)
  1140  
  1141  	closeFiles, err = shell.setRedirects(cmds[last], nodeCommands[last].Redirects())
  1142  	closeAfterWait = append(closeAfterWait, closeFiles...)
  1143  
  1144  	if err != nil {
  1145  		errIndex = last
  1146  		goto pipeError
  1147  	}
  1148  
  1149  	for i := 0; i < len(cmds); i++ {
  1150  		cmd := cmds[i]
  1151  
  1152  		err = cmd.Start()
  1153  
  1154  		if err != nil {
  1155  			errIndex = i
  1156  			goto pipeError
  1157  		}
  1158  
  1159  		errs[i] = "success"
  1160  		cods[i] = "0"
  1161  	}
  1162  
  1163  	for i, cmd := range cmds {
  1164  		err = cmd.Wait()
  1165  
  1166  		if err != nil {
  1167  			errIndex = i
  1168  			goto pipeError
  1169  		}
  1170  
  1171  		errs[i] = "success"
  1172  		cods[i] = "0"
  1173  	}
  1174  
  1175  	return sh.NewStrObj("0"), nil
  1176  
  1177  pipeError:
  1178  	if igns[errIndex] {
  1179  		errs[errIndex] = "none"
  1180  	} else {
  1181  		errs[errIndex] = err.Error()
  1182  	}
  1183  
  1184  	cods[errIndex] = getErrStatus(err, cods[errIndex])
  1185  
  1186  	err = errors.NewEvalError(shell.filename,
  1187  		pipe, strings.Join(errs, "|"))
  1188  
  1189  	// verify if all status codes are the same
  1190  	uniqCodes := make(map[string]struct{})
  1191  	var uniqCode string
  1192  
  1193  	for i := 0; i < len(cods); i++ {
  1194  		uniqCodes[cods[i]] = struct{}{}
  1195  		uniqCode = cods[i]
  1196  	}
  1197  
  1198  	var status sh.Obj
  1199  
  1200  	if len(uniqCodes) == 1 {
  1201  		// if all status are the same
  1202  		status = sh.NewStrObj(uniqCode)
  1203  	} else {
  1204  		status = sh.NewStrObj(strings.Join(cods, "|"))
  1205  	}
  1206  
  1207  	if igns[errIndex] {
  1208  		return status, nil
  1209  	}
  1210  
  1211  	return status, err
  1212  }
  1213  
  1214  func (shell *Shell) openRedirectLocation(location ast.Expr) (io.WriteCloser, error) {
  1215  	var protocol string
  1216  
  1217  	locationObj, err := shell.evalExpr(location)
  1218  	if err != nil {
  1219  		return nil, err
  1220  	}
  1221  
  1222  	if locationObj.Type() != sh.StringType {
  1223  		return nil, errors.NewEvalError(shell.filename,
  1224  			location,
  1225  			"Redirection to invalid object type: %v (%s)", locationObj, locationObj.Type())
  1226  	}
  1227  
  1228  	objstr := locationObj.(*sh.StrObj)
  1229  	locationStr := objstr.Str()
  1230  
  1231  	if len(locationStr) > 6 {
  1232  		if locationStr[0:6] == "tcp://" {
  1233  			protocol = "tcp"
  1234  		} else if locationStr[0:6] == "udp://" {
  1235  			protocol = "udp"
  1236  		} else if len(locationStr) > 7 && locationStr[0:7] == "unix://" {
  1237  			protocol = "unix"
  1238  		}
  1239  	}
  1240  
  1241  	if protocol == "" {
  1242  		return os.OpenFile(locationStr, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
  1243  	}
  1244  
  1245  	switch protocol {
  1246  	case "tcp", "udp":
  1247  		netParts := strings.Split(locationStr[6:], ":")
  1248  
  1249  		if len(netParts) != 2 {
  1250  			return nil, errors.NewEvalError(shell.filename,
  1251  				location,
  1252  				"Invalid tcp/udp address: %s", locationStr)
  1253  		}
  1254  
  1255  		url := netParts[0] + ":" + netParts[1]
  1256  
  1257  		return net.Dial(protocol, url)
  1258  	case "unix":
  1259  		return net.Dial(protocol, locationStr[7:])
  1260  	}
  1261  
  1262  	return nil, errors.NewEvalError(shell.filename, location,
  1263  		"Unexpected redirection value: %s", locationStr)
  1264  }
  1265  
  1266  func (shell *Shell) setRedirects(cmd sh.Runner, redirDecls []*ast.RedirectNode) ([]io.Closer, error) {
  1267  	var closeAfterWait []io.Closer
  1268  
  1269  	for _, r := range redirDecls {
  1270  		closeFiles, err := shell.buildRedirect(cmd, r)
  1271  		closeAfterWait = append(closeAfterWait, closeFiles...)
  1272  
  1273  		if err != nil {
  1274  			return closeAfterWait, err
  1275  		}
  1276  	}
  1277  
  1278  	return closeAfterWait, nil
  1279  }
  1280  
  1281  func (shell *Shell) buildRedirect(cmd sh.Runner, redirDecl *ast.RedirectNode) ([]io.Closer, error) {
  1282  	var closeAfterWait []io.Closer
  1283  
  1284  	if redirDecl.LeftFD() > 2 || redirDecl.LeftFD() < ast.RedirMapSupress {
  1285  		return closeAfterWait, errors.NewEvalError(shell.filename,
  1286  			redirDecl,
  1287  			"Invalid file descriptor redirection: fd=%d", redirDecl.LeftFD())
  1288  	}
  1289  
  1290  	if redirDecl.RightFD() > 2 || redirDecl.RightFD() < ast.RedirMapSupress {
  1291  		return closeAfterWait, errors.NewEvalError(shell.filename,
  1292  			redirDecl,
  1293  			"Invalid file descriptor redirection: fd=%d", redirDecl.RightFD())
  1294  	}
  1295  
  1296  	var err error
  1297  
  1298  	// Note(i4k): We need to remove the repetitive code in some smarter way
  1299  	switch redirDecl.LeftFD() {
  1300  	case 0:
  1301  		return closeAfterWait, fmt.Errorf("Does not support stdin redirection yet")
  1302  	case 1:
  1303  		switch redirDecl.RightFD() {
  1304  		case 0:
  1305  			return closeAfterWait, errors.NewEvalError(shell.filename,
  1306  				redirDecl,
  1307  				"Invalid redirect mapping: %d -> %d", 1, 0)
  1308  		case 1: // do nothing
  1309  		case 2:
  1310  			cmd.SetStdout(cmd.Stderr())
  1311  		case ast.RedirMapNoValue:
  1312  			if redirDecl.Location() == nil {
  1313  				return closeAfterWait, errors.NewEvalError(shell.filename,
  1314  					redirDecl,
  1315  					"Missing file in redirection: >[%d] <??>", redirDecl.LeftFD())
  1316  			}
  1317  
  1318  			file, err := shell.openRedirectLocation(redirDecl.Location())
  1319  			if err != nil {
  1320  				return closeAfterWait, err
  1321  			}
  1322  
  1323  			cmd.SetStdout(file)
  1324  			closeAfterWait = append(closeAfterWait, file)
  1325  		case ast.RedirMapSupress:
  1326  			file := ioutil.Discard
  1327  			cmd.SetStdout(file)
  1328  		}
  1329  	case 2:
  1330  		switch redirDecl.RightFD() {
  1331  		case 0:
  1332  			return closeAfterWait, errors.NewEvalError(shell.filename,
  1333  				redirDecl, "Invalid redirect mapping: %d -> %d", 2, 1)
  1334  		case 1:
  1335  			cmd.SetStderr(cmd.Stdout())
  1336  		case 2: // do nothing
  1337  		case ast.RedirMapNoValue:
  1338  			if redirDecl.Location() == nil {
  1339  				return closeAfterWait, errors.NewEvalError(shell.filename,
  1340  					redirDecl,
  1341  					"Missing file in redirection: >[%d] <??>", redirDecl.LeftFD())
  1342  			}
  1343  
  1344  			file, err := shell.openRedirectLocation(redirDecl.Location())
  1345  			if err != nil {
  1346  				return closeAfterWait, err
  1347  			}
  1348  
  1349  			cmd.SetStderr(file)
  1350  			closeAfterWait = append(closeAfterWait, file)
  1351  		case ast.RedirMapSupress:
  1352  			cmd.SetStderr(ioutil.Discard)
  1353  		}
  1354  	case ast.RedirMapNoValue:
  1355  		if redirDecl.Location() == nil {
  1356  			return closeAfterWait, errors.NewEvalError(shell.filename,
  1357  				redirDecl, "Missing file in redirection: >[%d] <??>", redirDecl.LeftFD())
  1358  		}
  1359  
  1360  		file, err := shell.openRedirectLocation(redirDecl.Location())
  1361  		if err != nil {
  1362  			return closeAfterWait, err
  1363  		}
  1364  
  1365  		cmd.SetStdout(file)
  1366  		closeAfterWait = append(closeAfterWait, file)
  1367  	}
  1368  
  1369  	return closeAfterWait, err
  1370  }
  1371  
  1372  func (shell *Shell) newBindfnRunner(
  1373  	c *ast.CommandNode,
  1374  	cmdName string,
  1375  	fnDef sh.FnDef,
  1376  ) (sh.Runner, error) {
  1377  	shell.logf("Executing bind %s", cmdName)
  1378  	shell.logf("%s bind to %s", cmdName, fnDef.Name())
  1379  
  1380  	if !shell.Interactive() {
  1381  		err := errors.NewEvalError(shell.filename,
  1382  			c, "'%s' is a bind to '%s'. "+
  1383  				"No binds allowed in non-interactive mode.",
  1384  			cmdName,
  1385  			fnDef.Name())
  1386  		return nil, err
  1387  	}
  1388  
  1389  	return fnDef.Build(), nil
  1390  }
  1391  
  1392  func (shell *Shell) getCommand(c *ast.CommandNode) (sh.Runner, bool, error) {
  1393  	var (
  1394  		ignoreError bool
  1395  		cmd         sh.Runner
  1396  		err         error
  1397  	)
  1398  
  1399  	cmdName := c.Name()
  1400  
  1401  	shell.logf("Executing: %s\n", c.Name())
  1402  
  1403  	if len(cmdName) > 1 && cmdName[0] == '-' {
  1404  		ignoreError = true
  1405  		cmdName = cmdName[1:]
  1406  
  1407  		shell.logf("Ignoring error\n")
  1408  	}
  1409  
  1410  	if cmdName == "" {
  1411  		return nil, false, errors.NewEvalError(shell.filename,
  1412  			c, "Empty command name...")
  1413  	}
  1414  
  1415  	if fnDef, ok := shell.Getbindfn(cmdName); ok {
  1416  		runner, err := shell.newBindfnRunner(c, cmdName, fnDef)
  1417  		return runner, ignoreError, err
  1418  	}
  1419  
  1420  	cmd, err = NewCmd(cmdName)
  1421  
  1422  	if err != nil {
  1423  		type NotFound interface {
  1424  			NotFound() bool
  1425  		}
  1426  
  1427  		shell.logf("Command fails: %s", err.Error())
  1428  
  1429  		if errNotFound, ok := err.(NotFound); ok && errNotFound.NotFound() {
  1430  			return nil, ignoreError, err
  1431  		}
  1432  
  1433  		return nil, ignoreError, err
  1434  	}
  1435  
  1436  	cmd.SetStdin(shell.stdin)
  1437  	cmd.SetStdout(shell.stdout)
  1438  	cmd.SetStderr(shell.stderr)
  1439  
  1440  	return cmd, ignoreError, nil
  1441  }
  1442  
  1443  func (shell *Shell) executeCommand(c *ast.CommandNode) (sh.Obj, error) {
  1444  	var (
  1445  		ignoreError    bool
  1446  		status         = "127"
  1447  		envVars        []string
  1448  		closeAfterWait []io.Closer
  1449  		cmd            sh.Runner
  1450  		err            error
  1451  		args           []sh.Obj
  1452  	)
  1453  
  1454  	defer func() {
  1455  		for _, c := range closeAfterWait {
  1456  			c.Close()
  1457  		}
  1458  	}()
  1459  
  1460  	cmd, ignoreError, err = shell.getCommand(c)
  1461  	if err != nil {
  1462  		goto cmdError
  1463  	}
  1464  
  1465  	// SetEnviron must be called before SetArgs
  1466  	// otherwise the subshell will have the arguments
  1467  	// shadowed by parent env
  1468  	envVars = buildenv(shell.Environ())
  1469  	cmd.SetEnviron(envVars)
  1470  
  1471  	args, err = shell.evalExprs(c.Args())
  1472  	if err != nil {
  1473  		goto cmdError
  1474  	}
  1475  
  1476  	err = cmd.SetArgs(args)
  1477  	if err != nil {
  1478  		goto cmdError
  1479  	}
  1480  
  1481  	closeAfterWait, err = shell.setRedirects(cmd, c.Redirects())
  1482  	if err != nil {
  1483  		goto cmdError
  1484  	}
  1485  
  1486  	err = cmd.Start()
  1487  	if err != nil {
  1488  		goto cmdError
  1489  	}
  1490  
  1491  	err = cmd.Wait()
  1492  	if err != nil {
  1493  		goto cmdError
  1494  	}
  1495  
  1496  	return sh.NewStrObj("0"), nil
  1497  
  1498  cmdError:
  1499  	statusObj := sh.NewStrObj(getErrStatus(err, status))
  1500  	if ignoreError {
  1501  		return statusObj, newErrIgnore(err.Error())
  1502  	}
  1503  
  1504  	return statusObj, err
  1505  }
  1506  
  1507  func (shell *Shell) evalList(argList *ast.ListExpr) (sh.Obj, error) {
  1508  	values := make([]sh.Obj, 0, len(argList.List))
  1509  
  1510  	for _, arg := range argList.List {
  1511  		obj, err := shell.evalExpr(arg)
  1512  		if err != nil {
  1513  			return nil, err
  1514  		}
  1515  
  1516  		values = append(values, obj)
  1517  	}
  1518  
  1519  	return sh.NewListObj(values), nil
  1520  }
  1521  
  1522  func (shell *Shell) evalArgList(argList *ast.ListExpr) ([]sh.Obj, error) {
  1523  	values := make([]sh.Obj, 0, len(argList.List))
  1524  
  1525  	for _, arg := range argList.List {
  1526  		obj, err := shell.evalExpr(arg)
  1527  		if err != nil {
  1528  			return nil, err
  1529  		}
  1530  
  1531  		values = append(values, obj)
  1532  	}
  1533  
  1534  	if argList.IsVariadic {
  1535  		return values, nil
  1536  	}
  1537  
  1538  	return []sh.Obj{sh.NewListObj(values)}, nil
  1539  }
  1540  
  1541  func (shell *Shell) evalIndex(index ast.Expr) (int, error) {
  1542  	if index.Type() != ast.NodeIntExpr && index.Type() != ast.NodeVarExpr && index.Type() != ast.NodeIndexExpr {
  1543  		return 0, errors.NewEvalError(shell.filename,
  1544  			index, "Invalid indexing type: %s", index.Type())
  1545  	}
  1546  
  1547  	if index.Type() == ast.NodeIntExpr {
  1548  		idxArg := index.(*ast.IntExpr)
  1549  		return idxArg.Value(), nil
  1550  	}
  1551  
  1552  	idxObj, err := shell.evalVariable(index)
  1553  	if err != nil {
  1554  		return 0, err
  1555  	}
  1556  
  1557  	if idxObj.Type() != sh.StringType {
  1558  		return 0, errors.NewEvalError(shell.filename,
  1559  			index, "Invalid object type on index value: %s", idxObj.Type())
  1560  	}
  1561  
  1562  	objstr := idxObj.(*sh.StrObj)
  1563  	indexNum, err := strconv.Atoi(objstr.Str())
  1564  
  1565  	if err != nil {
  1566  		return 0, err
  1567  	}
  1568  
  1569  	return indexNum, nil
  1570  }
  1571  
  1572  func (shell *Shell) evalIndexedVar(indexVar *ast.IndexExpr) (sh.Obj, error) {
  1573  	v, err := shell.evalVariable(indexVar.Var)
  1574  
  1575  	if err != nil {
  1576  		return nil, err
  1577  	}
  1578  
  1579  	col, err := sh.NewCollection(v)
  1580  	if err != nil {
  1581  		return nil, errors.NewEvalError(shell.filename, indexVar.Var, err.Error())
  1582  	}
  1583  
  1584  	indexNum, err := shell.evalIndex(indexVar.Index)
  1585  	if err != nil {
  1586  		return nil, err
  1587  	}
  1588  
  1589  	val, err := col.Get(indexNum)
  1590  	if err != nil {
  1591  		return nil, errors.NewEvalError(shell.filename, indexVar.Var, err.Error())
  1592  	}
  1593  	return val, nil
  1594  }
  1595  
  1596  func (shell *Shell) evalArgIndexedVar(indexVar *ast.IndexExpr) ([]sh.Obj, error) {
  1597  	v, err := shell.evalVariable(indexVar.Var)
  1598  	if err != nil {
  1599  		return nil, err
  1600  	}
  1601  
  1602  	col, err := sh.NewCollection(v)
  1603  	if err != nil {
  1604  		return nil, errors.NewEvalError(shell.filename, indexVar.Var, err.Error())
  1605  	}
  1606  
  1607  	indexNum, err := shell.evalIndex(indexVar.Index)
  1608  	if err != nil {
  1609  		return nil, err
  1610  	}
  1611  
  1612  	retval, err := col.Get(indexNum)
  1613  	if err != nil {
  1614  		return nil, errors.NewEvalError(shell.filename, indexVar.Var, err.Error())
  1615  	}
  1616  
  1617  	if indexVar.IsVariadic {
  1618  		if retval.Type() != sh.ListType {
  1619  			return nil, errors.NewEvalError(shell.filename,
  1620  				indexVar, "Use of '...' on a non-list variable")
  1621  		}
  1622  		retlist := retval.(*sh.ListObj)
  1623  		return retlist.List(), nil
  1624  	}
  1625  	return []sh.Obj{retval}, nil
  1626  }
  1627  
  1628  func (shell *Shell) evalVariable(a ast.Expr) (sh.Obj, error) {
  1629  	var (
  1630  		value sh.Obj
  1631  		ok    bool
  1632  	)
  1633  
  1634  	if a.Type() == ast.NodeIndexExpr {
  1635  		return shell.evalIndexedVar(a.(*ast.IndexExpr))
  1636  	}
  1637  
  1638  	if a.Type() != ast.NodeVarExpr {
  1639  		return nil, errors.NewEvalError(shell.filename,
  1640  			a, "Invalid eval of non variable argument: %s", a)
  1641  	}
  1642  
  1643  	vexpr := a.(*ast.VarExpr)
  1644  	varName := vexpr.Name
  1645  
  1646  	if value, ok = shell.Getvar(varName[1:]); !ok {
  1647  		return nil, errors.NewEvalError(shell.filename,
  1648  			a, "Variable %s not set on shell %s", varName, shell.name)
  1649  	}
  1650  	return value, nil
  1651  }
  1652  
  1653  func (shell *Shell) evalArgVariable(a ast.Expr) ([]sh.Obj, error) {
  1654  	var (
  1655  		value sh.Obj
  1656  		ok    bool
  1657  	)
  1658  
  1659  	if a.Type() == ast.NodeIndexExpr {
  1660  		return shell.evalArgIndexedVar(a.(*ast.IndexExpr))
  1661  	}
  1662  
  1663  	if a.Type() != ast.NodeVarExpr {
  1664  		return nil, errors.NewEvalError(shell.filename,
  1665  			a, "Invalid eval of non variable argument: %s", a)
  1666  	}
  1667  
  1668  	vexpr := a.(*ast.VarExpr)
  1669  	if value, ok = shell.Getvar(vexpr.Name[1:]); !ok {
  1670  		return nil, errors.NewEvalError(shell.filename,
  1671  			a, "Variable %s not set on shell %s", vexpr.Name,
  1672  			shell.name)
  1673  	}
  1674  
  1675  	if vexpr.IsVariadic {
  1676  		if value.Type() != sh.ListType {
  1677  			return nil, errors.NewEvalError(shell.filename,
  1678  				a, "Variable expansion (%s) on a non-list object",
  1679  				vexpr.String())
  1680  		}
  1681  
  1682  		return value.(*sh.ListObj).List(), nil
  1683  	}
  1684  
  1685  	return []sh.Obj{value}, nil
  1686  }
  1687  
  1688  func (shell *Shell) evalExprs(exprs []ast.Expr) ([]sh.Obj, error) {
  1689  	objs := make([]sh.Obj, 0, len(exprs))
  1690  
  1691  	for _, expr := range exprs {
  1692  		obj, err := shell.evalExpr(expr)
  1693  		if err != nil {
  1694  			return nil, err
  1695  		}
  1696  
  1697  		objs = append(objs, obj)
  1698  	}
  1699  
  1700  	return objs, nil
  1701  }
  1702  
  1703  func (shell *Shell) evalArgExprs(exprs []ast.Expr) ([]sh.Obj, error) {
  1704  	ret := make([]sh.Obj, 0, len(exprs))
  1705  
  1706  	for _, expr := range exprs {
  1707  		objs, err := shell.evalArgExpr(expr)
  1708  		if err != nil {
  1709  			return nil, err
  1710  		}
  1711  
  1712  		ret = append(ret, objs...)
  1713  	}
  1714  
  1715  	return ret, nil
  1716  }
  1717  
  1718  func (shell *Shell) evalArgExpr(expr ast.Expr) ([]sh.Obj, error) {
  1719  	switch expr.Type() {
  1720  	case ast.NodeStringExpr:
  1721  		if str, ok := expr.(*ast.StringExpr); ok {
  1722  			return []sh.Obj{
  1723  				sh.NewStrObj(str.Value()),
  1724  			}, nil
  1725  		}
  1726  	case ast.NodeConcatExpr:
  1727  		if concat, ok := expr.(*ast.ConcatExpr); ok {
  1728  			argVal, err := shell.evalConcat(concat)
  1729  			if err != nil {
  1730  				return nil, err
  1731  			}
  1732  
  1733  			return []sh.Obj{
  1734  				sh.NewStrObj(argVal),
  1735  			}, nil
  1736  		}
  1737  	case ast.NodeVarExpr:
  1738  		return shell.evalArgVariable(expr)
  1739  	case ast.NodeIndexExpr:
  1740  		if indexedVar, ok := expr.(*ast.IndexExpr); ok {
  1741  			return shell.evalArgIndexedVar(indexedVar)
  1742  		}
  1743  	case ast.NodeListExpr:
  1744  		if listExpr, ok := expr.(*ast.ListExpr); ok {
  1745  			return shell.evalArgList(listExpr)
  1746  		}
  1747  	case ast.NodeFnInv:
  1748  		if fnInv, ok := expr.(*ast.FnInvNode); ok {
  1749  			objs, err := shell.executeFnInv(fnInv)
  1750  			if err != nil {
  1751  				return nil, err
  1752  			}
  1753  
  1754  			if len(objs) == 0 {
  1755  				return nil, errors.NewEvalError(shell.filename,
  1756  					expr,
  1757  					"Function used in"+
  1758  						" expression but do not return any value: %s",
  1759  					fnInv)
  1760  			} else if len(objs) != 1 {
  1761  				return nil, errors.NewEvalError(shell.filename,
  1762  					expr,
  1763  					"Function used in"+
  1764  						" expression but it returns %d values: %10q",
  1765  					len(objs), objs)
  1766  			}
  1767  
  1768  			return []sh.Obj{objs[0]}, nil
  1769  		}
  1770  	}
  1771  
  1772  	return nil, errors.NewEvalError(shell.filename,
  1773  		expr, "Failed to eval expression: %+v", expr)
  1774  }
  1775  
  1776  func (shell *Shell) evalExpr(expr ast.Expr) (sh.Obj, error) {
  1777  	switch expr.Type() {
  1778  	case ast.NodeStringExpr:
  1779  		if str, ok := expr.(*ast.StringExpr); ok {
  1780  			return sh.NewStrObj(str.Value()), nil
  1781  		}
  1782  	case ast.NodeConcatExpr:
  1783  		if concat, ok := expr.(*ast.ConcatExpr); ok {
  1784  			argVal, err := shell.evalConcat(concat)
  1785  			if err != nil {
  1786  				return nil, err
  1787  			}
  1788  
  1789  			return sh.NewStrObj(argVal), nil
  1790  		}
  1791  	case ast.NodeVarExpr:
  1792  		return shell.evalVariable(expr)
  1793  	case ast.NodeIndexExpr:
  1794  		if indexedVar, ok := expr.(*ast.IndexExpr); ok {
  1795  			return shell.evalIndexedVar(indexedVar)
  1796  		}
  1797  	case ast.NodeListExpr:
  1798  		if listExpr, ok := expr.(*ast.ListExpr); ok {
  1799  			return shell.evalList(listExpr)
  1800  		}
  1801  	case ast.NodeFnInv:
  1802  		if fnInv, ok := expr.(*ast.FnInvNode); ok {
  1803  			objs, err := shell.executeFnInv(fnInv)
  1804  			if err != nil {
  1805  				return nil, err
  1806  			}
  1807  
  1808  			if len(objs) == 0 {
  1809  				return nil, errors.NewEvalError(shell.filename,
  1810  					expr,
  1811  					"Function used in"+
  1812  						" expression but do not return any value: %s",
  1813  					fnInv)
  1814  			} else if len(objs) != 1 {
  1815  				return nil, errors.NewEvalError(shell.filename,
  1816  					expr,
  1817  					"Function used in"+
  1818  						" expression but it returns %d values: %10q",
  1819  					len(objs), objs)
  1820  			}
  1821  
  1822  			return objs[0], nil
  1823  		}
  1824  	}
  1825  
  1826  	return nil, errors.NewEvalError(shell.filename,
  1827  		expr, "Failed to eval expression: %+v", expr)
  1828  }
  1829  
  1830  func (shell *Shell) executeSetenvAssign(assign *ast.AssignNode) error {
  1831  	for i := 0; i < len(assign.Names); i++ {
  1832  		name := assign.Names[i]
  1833  		value := assign.Values[i]
  1834  		err := shell.initVar(name, value)
  1835  		if err != nil {
  1836  			return err
  1837  		}
  1838  		obj, ok := shell.GetLocalvar(name.Ident)
  1839  		if !ok {
  1840  			return errors.NewEvalError(shell.filename,
  1841  				assign,
  1842  				"internal error: Setenv not setting local variable '%s'",
  1843  				name.Ident,
  1844  			)
  1845  		}
  1846  		shell.Setenv(name.Ident, obj)
  1847  	}
  1848  	return nil
  1849  }
  1850  
  1851  func (shell *Shell) executeSetenvExec(assign *ast.ExecAssignNode) error {
  1852  	err := shell.executeExecAssign(assign)
  1853  	if err != nil {
  1854  		return err
  1855  	}
  1856  	for i := 0; i < len(assign.Names); i++ {
  1857  		name := assign.Names[i]
  1858  		obj, ok := shell.GetLocalvar(name.Ident)
  1859  		if !ok {
  1860  			return errors.NewEvalError(shell.filename,
  1861  				assign,
  1862  				"internal error: Setenv not setting local variable '%s'",
  1863  				name.Ident,
  1864  			)
  1865  		}
  1866  		shell.Setenv(name.Ident, obj)
  1867  	}
  1868  	return nil
  1869  }
  1870  
  1871  func (shell *Shell) executeSetenv(v *ast.SetenvNode) error {
  1872  	var (
  1873  		varValue sh.Obj
  1874  		ok       bool
  1875  		assign   = v.Assignment()
  1876  	)
  1877  
  1878  	if assign != nil {
  1879  		switch assign.Type() {
  1880  		case ast.NodeAssign:
  1881  			return shell.executeSetenvAssign(assign.(*ast.AssignNode))
  1882  		case ast.NodeExecAssign:
  1883  			return shell.executeSetenvExec(assign.(*ast.ExecAssignNode))
  1884  		}
  1885  		return errors.NewEvalError(shell.filename,
  1886  			v, "Failed to eval setenv, invalid assignment type: %+v",
  1887  			assign)
  1888  	}
  1889  
  1890  	varValue, ok = shell.Getvar(v.Name)
  1891  	if !ok {
  1892  		return errors.NewEvalError(shell.filename,
  1893  			v, "Variable '%s' not set on shell %s", v.Name,
  1894  			shell.name,
  1895  		)
  1896  	}
  1897  	shell.Setenv(v.Name, varValue)
  1898  	return nil
  1899  }
  1900  
  1901  func (shell *Shell) concatElements(expr *ast.ConcatExpr) (string, error) {
  1902  	value := ""
  1903  
  1904  	list := expr.List()
  1905  
  1906  	for i := 0; i < len(list); i++ {
  1907  		ec := list[i]
  1908  
  1909  		obj, err := shell.evalExpr(ec)
  1910  
  1911  		if err != nil {
  1912  			return "", err
  1913  		}
  1914  
  1915  		if obj.Type() != sh.StringType {
  1916  			return "", errors.NewEvalError(shell.filename,
  1917  				expr, "Impossible to concat elements of type %s", obj.Type())
  1918  		}
  1919  
  1920  		value = value + obj.String()
  1921  	}
  1922  
  1923  	return value, nil
  1924  }
  1925  
  1926  func (shell *Shell) execCmdOutput(cmd ast.Node,
  1927  	getstderr, ignoreError bool) ([]byte, []byte, sh.Obj, error) {
  1928  	var (
  1929  		outBuf, errBuf bytes.Buffer
  1930  		err            error
  1931  		status         sh.Obj
  1932  	)
  1933  	if cmd.Type() != ast.NodeCommand &&
  1934  		cmd.Type() != ast.NodePipe {
  1935  		return nil, nil, nil, errors.NewEvalError(shell.filename,
  1936  			cmd, "Invalid node type (%v). Expected command or pipe",
  1937  			cmd)
  1938  	}
  1939  
  1940  	bkStdout, bkStderr := shell.stdout, shell.stderr
  1941  	shell.SetStdout(&outBuf)
  1942  	if getstderr {
  1943  		shell.SetStderr(&errBuf)
  1944  	}
  1945  	defer func() {
  1946  		shell.SetStdout(bkStdout)
  1947  		shell.SetStderr(bkStderr)
  1948  	}()
  1949  
  1950  	if cmd.Type() == ast.NodeCommand {
  1951  		status, err = shell.executeCommand(cmd.(*ast.CommandNode))
  1952  	} else {
  1953  		status, err = shell.executePipe(cmd.(*ast.PipeNode))
  1954  	}
  1955  
  1956  	outb := outBuf.Bytes()
  1957  	errb := errBuf.Bytes()
  1958  
  1959  	trimnl := func(data []byte) []byte {
  1960  		if len(data) > 0 && data[len(data)-1] == '\n' {
  1961  			// remove the trailing new line
  1962  			// Why? because it's what user wants in 99.99% of times...
  1963  
  1964  			data = data[0 : len(data)-1]
  1965  		}
  1966  		return data[:]
  1967  	}
  1968  
  1969  	if ignoreError {
  1970  		err = nil
  1971  	}
  1972  
  1973  	return trimnl(outb), trimnl(errb), status, err
  1974  }
  1975  
  1976  func (shell *Shell) executeExecAssignCmd(v ast.Node) (stdout, stderr, status sh.Obj, err error) {
  1977  	assign := v.(*ast.ExecAssignNode)
  1978  	cmd := assign.Command()
  1979  
  1980  	mustIgnoreErr := len(assign.Names) > 1
  1981  	collectStderr := len(assign.Names) == 3
  1982  
  1983  	outb, errb, status, err := shell.execCmdOutput(cmd, collectStderr, mustIgnoreErr)
  1984  	if err != nil {
  1985  		return nil, nil, nil, err
  1986  	}
  1987  
  1988  	return sh.NewStrObj(string(outb)), sh.NewStrObj(string(errb)), status, nil
  1989  }
  1990  
  1991  func (shell *Shell) executeExecAssignFn(assign *ast.ExecAssignNode) ([]sh.Obj, error) {
  1992  	var (
  1993  		err      error
  1994  		fnValues []sh.Obj
  1995  	)
  1996  
  1997  	cmd := assign.Command()
  1998  	if cmd.Type() != ast.NodeFnInv {
  1999  		return nil, errors.NewEvalError(shell.filename,
  2000  			cmd, "Invalid node type (%v). Expected function call",
  2001  			cmd)
  2002  	}
  2003  
  2004  	fnValues, err = shell.executeFnInv(cmd.(*ast.FnInvNode))
  2005  	if err != nil {
  2006  		return nil, err
  2007  	}
  2008  
  2009  	if len(fnValues) != len(assign.Names) {
  2010  		return nil, errors.NewEvalError(shell.filename,
  2011  			assign, "Functions returns %d objects, but statement expects %d",
  2012  			len(fnValues), len(assign.Names))
  2013  	}
  2014  
  2015  	return fnValues, nil
  2016  }
  2017  
  2018  func (shell *Shell) executeExecAssign(v *ast.ExecAssignNode) (err error) {
  2019  	exec := v.Command()
  2020  	switch exec.Type() {
  2021  	case ast.NodeFnInv:
  2022  		var values []sh.Obj
  2023  		values, err = shell.executeExecAssignFn(v)
  2024  		if err != nil {
  2025  			return err
  2026  		}
  2027  		err = shell.setvars(v.Names, values)
  2028  	case ast.NodeCommand, ast.NodePipe:
  2029  		var stdout, stderr, status sh.Obj
  2030  		stdout, stderr, status, err = shell.executeExecAssignCmd(v)
  2031  		if err != nil {
  2032  			return err
  2033  		}
  2034  
  2035  		err = shell.setcmdvars(v.Names, stdout, stderr, status)
  2036  	default:
  2037  		err = errors.NewEvalError(shell.filename,
  2038  			exec, "Invalid node type (%v). Expected function call, command or pipe",
  2039  			exec)
  2040  	}
  2041  
  2042  	return err
  2043  }
  2044  
  2045  func (shell *Shell) initVar(name *ast.NameNode, value ast.Expr) error {
  2046  	obj, err := shell.evalExpr(value)
  2047  	if err != nil {
  2048  		return err
  2049  	}
  2050  	return shell.newvar(name, obj)
  2051  }
  2052  
  2053  func (shell *Shell) executeVarAssign(v *ast.VarAssignDeclNode) error {
  2054  	assign := v.Assign
  2055  	if len(assign.Names) != len(assign.Values) {
  2056  		return errors.NewEvalError(shell.filename,
  2057  			assign, "Invalid multiple assignment. Different amount of variables and values: %s",
  2058  			assign,
  2059  		)
  2060  	}
  2061  
  2062  	for i := 0; i < len(assign.Names); i++ {
  2063  		name := assign.Names[i]
  2064  		value := assign.Values[i]
  2065  
  2066  		err := shell.initVar(name, value)
  2067  		if err != nil {
  2068  			return err
  2069  		}
  2070  	}
  2071  
  2072  	return nil
  2073  }
  2074  
  2075  func (shell *Shell) executeVarExecAssign(v *ast.VarExecAssignDeclNode) (err error) {
  2076  	assign := v.ExecAssign
  2077  	exec := assign.Command()
  2078  	switch exec.Type() {
  2079  	case ast.NodeFnInv:
  2080  		var values []sh.Obj
  2081  		values, err = shell.executeExecAssignFn(assign)
  2082  		if err != nil {
  2083  			return err
  2084  		}
  2085  		shell.newvars(assign.Names, values)
  2086  	case ast.NodeCommand, ast.NodePipe:
  2087  		var stdout, stderr, status sh.Obj
  2088  		stdout, stderr, status, err = shell.executeExecAssignCmd(assign)
  2089  		if err != nil {
  2090  			return err
  2091  		}
  2092  
  2093  		shell.newcmdvars(assign.Names, stdout, stderr, status)
  2094  	default:
  2095  		err = errors.NewEvalError(shell.filename,
  2096  			exec, "Invalid node type (%v). Expected function call, command or pipe",
  2097  			exec)
  2098  	}
  2099  
  2100  	return err
  2101  }
  2102  
  2103  func (shell *Shell) executeAssignment(v *ast.AssignNode) error {
  2104  	if len(v.Names) != len(v.Values) {
  2105  		return errors.NewEvalError(shell.filename,
  2106  			v, "Invalid multiple assignment. Different amount of variables and values: %s",
  2107  			v,
  2108  		)
  2109  	}
  2110  
  2111  	for i := 0; i < len(v.Names); i++ {
  2112  		name := v.Names[i]
  2113  		value := v.Values[i]
  2114  
  2115  		obj, err := shell.evalExpr(value)
  2116  		if err != nil {
  2117  			return err
  2118  		}
  2119  
  2120  		err = shell.setvar(name, obj)
  2121  		if err != nil {
  2122  			return err
  2123  		}
  2124  	}
  2125  
  2126  	return nil
  2127  }
  2128  
  2129  func (shell *Shell) evalIfArgument(arg ast.Node) (sh.Obj, error) {
  2130  	var (
  2131  		obj sh.Obj
  2132  		err error
  2133  	)
  2134  
  2135  	obj, err = shell.evalExpr(arg)
  2136  	if err != nil {
  2137  		return nil, err
  2138  	} else if obj == nil {
  2139  		return nil, errors.NewEvalError(shell.filename,
  2140  			arg, "lvalue doesn't yield value (%s)", arg)
  2141  	}
  2142  
  2143  	return obj, nil
  2144  }
  2145  
  2146  func (shell *Shell) evalIfArguments(n *ast.IfNode) (string, string, error) {
  2147  	var (
  2148  		lobj, robj sh.Obj
  2149  		err        error
  2150  	)
  2151  
  2152  	lobj, err = shell.evalIfArgument(n.Lvalue())
  2153  
  2154  	if err != nil {
  2155  		return "", "", err
  2156  	}
  2157  
  2158  	robj, err = shell.evalIfArgument(n.Rvalue())
  2159  
  2160  	if err != nil {
  2161  		return "", "", err
  2162  	}
  2163  
  2164  	if lobj.Type() != sh.StringType {
  2165  		return "", "", errors.NewEvalError(shell.filename,
  2166  			n, "lvalue is not comparable: (%v) -> %s.", lobj, lobj.Type())
  2167  	}
  2168  
  2169  	if robj.Type() != sh.StringType {
  2170  		return "", "", errors.NewEvalError(shell.filename,
  2171  			n, "rvalue is not comparable: (%v) -> %s.", lobj, lobj.Type())
  2172  	}
  2173  
  2174  	lobjstr := lobj.(*sh.StrObj)
  2175  	robjstr := robj.(*sh.StrObj)
  2176  
  2177  	return lobjstr.Str(), robjstr.Str(), nil
  2178  }
  2179  
  2180  func (shell *Shell) executeIfEqual(n *ast.IfNode) ([]sh.Obj, error) {
  2181  	lstr, rstr, err := shell.evalIfArguments(n)
  2182  
  2183  	if err != nil {
  2184  		return nil, err
  2185  	}
  2186  
  2187  	if lstr == rstr {
  2188  		return shell.executeTree(n.IfTree(), false)
  2189  	} else if n.ElseTree() != nil {
  2190  		return shell.executeTree(n.ElseTree(), false)
  2191  	}
  2192  
  2193  	return nil, nil
  2194  }
  2195  
  2196  func (shell *Shell) executeIfNotEqual(n *ast.IfNode) ([]sh.Obj, error) {
  2197  	lstr, rstr, err := shell.evalIfArguments(n)
  2198  
  2199  	if err != nil {
  2200  		return nil, err
  2201  	}
  2202  
  2203  	if lstr != rstr {
  2204  		return shell.executeTree(n.IfTree(), false)
  2205  	} else if n.ElseTree() != nil {
  2206  		return shell.executeTree(n.ElseTree(), false)
  2207  	}
  2208  
  2209  	return nil, nil
  2210  }
  2211  
  2212  func (shell *Shell) executeFnInv(n *ast.FnInvNode) ([]sh.Obj, error) {
  2213  	var fnDef sh.FnDef
  2214  
  2215  	fnName := n.Name()
  2216  	if len(fnName) > 1 && fnName[0] == '$' {
  2217  		argVar := ast.NewVarExpr(token.NewFileInfo(n.Line(), n.Column()), fnName)
  2218  
  2219  		obj, err := shell.evalVariable(argVar)
  2220  		if err != nil {
  2221  			return nil, err
  2222  		}
  2223  
  2224  		if obj.Type() != sh.FnType {
  2225  			return nil, errors.NewEvalError(shell.filename,
  2226  				n, "Variable '%s' is not a function.", fnName)
  2227  		}
  2228  
  2229  		objfn := obj.(*sh.FnObj)
  2230  		fnDef = objfn.Fn()
  2231  	} else {
  2232  		fnObj, err := shell.GetFn(fnName)
  2233  		if err != nil {
  2234  			return nil, errors.NewEvalError(shell.filename,
  2235  				n, err.Error())
  2236  		}
  2237  		fnDef = fnObj.Fn()
  2238  	}
  2239  
  2240  	fn := fnDef.Build()
  2241  	args, err := shell.evalArgExprs(n.Args())
  2242  	if err != nil {
  2243  		return nil, err
  2244  	}
  2245  
  2246  	err = fn.SetArgs(args)
  2247  	if err != nil {
  2248  		return nil, errors.NewEvalError(shell.filename,
  2249  			n, err.Error())
  2250  	}
  2251  
  2252  	fn.SetStdin(shell.stdin)
  2253  	fn.SetStdout(shell.stdout)
  2254  	fn.SetStderr(shell.stderr)
  2255  
  2256  	err = fn.Start()
  2257  	if err != nil {
  2258  		return nil, errors.NewEvalError(shell.filename,
  2259  			n, err.Error())
  2260  	}
  2261  
  2262  	err = fn.Wait()
  2263  	if err != nil {
  2264  		return nil, errors.NewEvalError(shell.filename,
  2265  			n, err.Error())
  2266  	}
  2267  
  2268  	return fn.Results(), nil
  2269  }
  2270  
  2271  func (shell *Shell) executeInfLoop(tr *ast.Tree) ([]sh.Obj, error) {
  2272  	var (
  2273  		err  error
  2274  		objs []sh.Obj
  2275  	)
  2276  
  2277  	for {
  2278  		objs, err = shell.executeTree(tr, false)
  2279  
  2280  		runtime.Gosched()
  2281  
  2282  		type (
  2283  			interruptedError interface {
  2284  				Interrupted() bool
  2285  			}
  2286  
  2287  			stopWalkingError interface {
  2288  				StopWalking() bool
  2289  			}
  2290  		)
  2291  
  2292  		if errInterrupted, ok := err.(interruptedError); ok && errInterrupted.Interrupted() {
  2293  			break
  2294  		}
  2295  
  2296  		if errStopWalking, ok := err.(stopWalkingError); ok && errStopWalking.StopWalking() {
  2297  			return objs, err
  2298  		}
  2299  
  2300  		shell.Lock()
  2301  
  2302  		if shell.getIntr() {
  2303  			shell.setIntr(false)
  2304  
  2305  			if err != nil {
  2306  				err = newErrInterrupted(err.Error())
  2307  			} else {
  2308  				err = newErrInterrupted("loop interrupted")
  2309  			}
  2310  		}
  2311  
  2312  		shell.Unlock()
  2313  
  2314  		if err != nil {
  2315  			break
  2316  		}
  2317  	}
  2318  
  2319  	return nil, err
  2320  }
  2321  
  2322  func (shell *Shell) executeFor(n *ast.ForNode) ([]sh.Obj, error) {
  2323  	shell.Lock()
  2324  	shell.looping = true
  2325  	shell.Unlock()
  2326  
  2327  	defer func() {
  2328  		shell.Lock()
  2329  		defer shell.Unlock()
  2330  
  2331  		shell.looping = false
  2332  	}()
  2333  
  2334  	if n.InExpr() == nil {
  2335  		return shell.executeInfLoop(n.Tree())
  2336  	}
  2337  
  2338  	id := n.Identifier()
  2339  	inExpr := n.InExpr()
  2340  
  2341  	var (
  2342  		obj sh.Obj
  2343  		err error
  2344  	)
  2345  
  2346  	if inExpr.Type() == ast.NodeVarExpr {
  2347  		obj, err = shell.evalVariable(inExpr.(*ast.VarExpr))
  2348  	} else if inExpr.Type() == ast.NodeListExpr {
  2349  		obj, err = shell.evalList(inExpr.(*ast.ListExpr))
  2350  	} else if inExpr.Type() == ast.NodeFnInv {
  2351  		var objs []sh.Obj
  2352  		objs, err = shell.executeFnInv(inExpr.(*ast.FnInvNode))
  2353  		if err != nil {
  2354  			return nil, err
  2355  		}
  2356  
  2357  		if len(objs) != 1 {
  2358  			return nil, errors.NewEvalError(shell.filename,
  2359  				inExpr, "Functions with multiple returns do not work as for 'in expression' yet: %v", inExpr)
  2360  		}
  2361  
  2362  		obj = objs[0]
  2363  	} else {
  2364  		return nil, errors.NewEvalError(shell.filename,
  2365  			inExpr, "Invalid expression in for loop: %s", inExpr.Type())
  2366  	}
  2367  
  2368  	if err != nil {
  2369  		return nil, err
  2370  	}
  2371  
  2372  	col, err := sh.NewCollection(obj)
  2373  	if err != nil {
  2374  		return nil, errors.NewEvalError(shell.filename,
  2375  			inExpr, "error[%s] trying to iterate", err)
  2376  	}
  2377  
  2378  	for i := 0; i < col.Len(); i++ {
  2379  		val, err := col.Get(i)
  2380  		if err != nil {
  2381  			return nil, errors.NewEvalError(shell.filename,
  2382  				inExpr, "unexpected error[%s] during iteration", err)
  2383  		}
  2384  		shell.Newvar(id, val)
  2385  		objs, err := shell.executeTree(n.Tree(), false)
  2386  
  2387  		type (
  2388  			interruptedError interface {
  2389  				Interrupted() bool
  2390  			}
  2391  
  2392  			stopWalkingError interface {
  2393  				StopWalking() bool
  2394  			}
  2395  		)
  2396  
  2397  		if errInterrupted, ok := err.(interruptedError); ok && errInterrupted.Interrupted() {
  2398  			return nil, err
  2399  		}
  2400  
  2401  		if errStopWalking, ok := err.(stopWalkingError); ok && errStopWalking.StopWalking() {
  2402  			return objs, err
  2403  		}
  2404  
  2405  		shell.Lock()
  2406  
  2407  		if shell.getIntr() {
  2408  			shell.setIntr(false)
  2409  			shell.Unlock()
  2410  
  2411  			if err != nil {
  2412  				return nil, newErrInterrupted(err.Error())
  2413  			}
  2414  
  2415  			return nil, newErrInterrupted("loop interrupted")
  2416  		}
  2417  
  2418  		shell.Unlock()
  2419  
  2420  		if err != nil {
  2421  			return nil, err
  2422  		}
  2423  	}
  2424  
  2425  	return nil, nil
  2426  }
  2427  
  2428  func (shell *Shell) executeFnDecl(n *ast.FnDeclNode) error {
  2429  	fnDef, err := newUserFnDef(n.Name(), shell, n.Args(), n.Tree())
  2430  	if err != nil {
  2431  		return err
  2432  	}
  2433  
  2434  	shell.Newvar(n.Name(), sh.NewFnObj(fnDef))
  2435  	shell.logf("Function %s declared on '%s'", n.Name(), shell.name)
  2436  	return nil
  2437  }
  2438  
  2439  func (shell *Shell) executeBindFn(n *ast.BindFnNode) error {
  2440  	if !shell.Interactive() {
  2441  		return errors.NewEvalError(shell.filename,
  2442  			n, "'bindfn' is not allowed in non-interactive mode.")
  2443  	}
  2444  
  2445  	fnDef, err := shell.GetFn(n.Name())
  2446  	if err != nil {
  2447  		return errors.NewEvalError(shell.filename,
  2448  			n, err.Error())
  2449  	}
  2450  
  2451  	shell.Setbindfn(n.CmdName(), fnDef.Fn())
  2452  	return nil
  2453  }
  2454  
  2455  func (shell *Shell) executeIf(n *ast.IfNode) ([]sh.Obj, error) {
  2456  	op := n.Op()
  2457  
  2458  	if op == "==" {
  2459  		return shell.executeIfEqual(n)
  2460  	} else if op == "!=" {
  2461  		return shell.executeIfNotEqual(n)
  2462  	}
  2463  
  2464  	return nil, fmt.Errorf("invalid operation '%s'", op)
  2465  }
  2466  
  2467  func validateDirs(nashpath string, nashroot string) error {
  2468  	if nashpath == nashroot {
  2469  		return fmt.Errorf("invalid nashpath and nashroot, they are both[%s] but they must differ", nashpath)
  2470  	}
  2471  	err := validateDir(nashpath)
  2472  	if err != nil {
  2473  		return fmt.Errorf("invalid nashpath, user's config won't be loaded: error: %s", err)
  2474  	}
  2475  	err = validateDir(nashroot)
  2476  	if err != nil {
  2477  		return fmt.Errorf("invalid nashroot, stdlib/stdbin won't be available: error: %s", err)
  2478  	}
  2479  	return nil
  2480  }
  2481  
  2482  func validateDir(dir string) error {
  2483  	dir, err := filepath.EvalSymlinks(dir)
  2484  	if err != nil {
  2485  		return err
  2486  	}
  2487  
  2488  	info, err := os.Stat(dir)
  2489  	if err != nil {
  2490  		return err
  2491  	}
  2492  	if !info.IsDir() {
  2493  		return fmt.Errorf("%s is a file, expected a dir", dir)
  2494  	}
  2495  	if !filepath.IsAbs(dir) {
  2496  		return fmt.Errorf("%s is a relative path, expected a absolute path", dir)
  2497  	}
  2498  	return nil
  2499  }