github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/variables.go (about)

     1  package lang
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/lmorg/murex/builtins/pipes/streams"
    12  	"github.com/lmorg/murex/lang/ref"
    13  	"github.com/lmorg/murex/lang/types"
    14  	"github.com/lmorg/murex/utils/alter"
    15  	"github.com/lmorg/murex/utils/envvars"
    16  	"github.com/lmorg/murex/utils/json"
    17  	"github.com/lmorg/murex/utils/lists"
    18  )
    19  
    20  func errVariableReserved(name string) error {
    21  	return fmt.Errorf("cannot set a reserved variable: %s", name)
    22  }
    23  
    24  const ErrDoesNotExist = "does not exist"
    25  
    26  func errVarNotExist(name string) error {
    27  	return fmt.Errorf("variable '%s' %s", name, ErrDoesNotExist)
    28  }
    29  
    30  func errVarCannotUpdateNested(name string, err error) error {
    31  	return fmt.Errorf("cannot update element inside %s: %s", name, err.Error())
    32  }
    33  
    34  func errVarCannotUpdateIndexOrElement(name string) error {
    35  	return fmt.Errorf("cannot update `[ indexes ]` nor `[[ elements ]]`, these are immutable objects.\nPlease reference values using dot notation instead, eg $variable_name.path.to.element\nVariable  : %s", name)
    36  }
    37  
    38  func errVarNoParam(i int, err error) error {
    39  	return fmt.Errorf("variable '%d' is not set because the scope returned the following error when querying parameter %d: %s", i, i, err.Error())
    40  }
    41  
    42  func errVarZeroLengthPath(name string) error {
    43  	return fmt.Errorf("zero length path in variable name `%s`", name)
    44  }
    45  
    46  func errVarCannotGetProperty(name, path string, err error) error {
    47  	return fmt.Errorf("cannot get property '%s' for variable '%s'.\n%s.\nif this wasn't intended to be a property then try surrounding your variable name with parenthesis, eg:\n`  ...  $(%s).%s  ...  `",
    48  		path, name, err.Error(), name, path)
    49  }
    50  
    51  func errVarCannotStoreVariable(name string, err error) error {
    52  	return fmt.Errorf("cannot store variable '%s': %s", name, err.Error())
    53  }
    54  
    55  // Reserved variable names. Set as constants so any typos of these names within
    56  // the code will be raised as compiler errors
    57  const (
    58  	DOT        = ""
    59  	SELF       = "SELF"
    60  	ARGV       = "ARGV"
    61  	ARGS       = "ARGS"
    62  	PARAMS     = "PARAMS"
    63  	MUREX_EXE  = "MUREX_EXE"
    64  	MUREX_ARGS = "MUREX_ARGS"
    65  	MUREX_ARGV = "MUREX_ARGV"
    66  	HOSTNAME   = "HOSTNAME"
    67  	PWD        = "PWD"
    68  	ENV        = "ENV"
    69  	GLOBAL     = "GLOBAL"
    70  	MODULE     = "MOD"
    71  	COLUMNS    = "COLUMNS"
    72  )
    73  
    74  var ReservedVariableNames = []string{
    75  	SELF, ARGV, ARGS, PARAMS, MUREX_EXE, MUREX_ARGS, MUREX_ARGV,
    76  	HOSTNAME, PWD, ENV, MODULE, GLOBAL, COLUMNS,
    77  }
    78  
    79  // Variables is a table of all the variables. This will be local to the scope's
    80  // process
    81  type Variables struct {
    82  	process *Process // only needed for variables
    83  	vars    map[string]*variable
    84  	mutex   sync.Mutex
    85  	global  bool
    86  }
    87  
    88  // NewVariables creates a new variable table
    89  func NewVariables(p *Process) *Variables {
    90  	v := new(Variables)
    91  	v.vars = make(map[string]*variable)
    92  	v.process = p
    93  	return v
    94  }
    95  
    96  // NewGlobals creates a new global variable table
    97  func NewGlobals() *Variables {
    98  	v := new(Variables)
    99  	v.vars = make(map[string]*variable)
   100  	v.process = ShellProcess
   101  	v.global = true
   102  	return v
   103  }
   104  
   105  // variable is an individual variable or global variable
   106  type variable struct {
   107  	DataType    string
   108  	Value       interface{}
   109  	String      string
   110  	IsInterface bool
   111  	Modify      time.Time
   112  	FileRef     *ref.File // only needed for globals
   113  }
   114  
   115  // GetValue return the value of a variable. If a variable does not exist then
   116  // GetValue will return nil. Please check if p.Config.Get("proc", "strict-vars", "bool")
   117  // matters for your usage of GetValue because this API doesn't care. If in doubt
   118  // use GetString instead.
   119  func (v *Variables) GetValue(path string) (interface{}, error) {
   120  	if path == "." {
   121  		return v.getValue(DOT)
   122  	}
   123  
   124  	split := strings.Split(path, ".")
   125  	switch len(split) {
   126  	case 0:
   127  		return nil, errVarZeroLengthPath(path)
   128  	case 1:
   129  		return v.getValue(split[0])
   130  	default:
   131  		val, err := v.getValue(split[0])
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  
   136  		propertyPath := strings.Join(split[1:], ".")
   137  		value, err := ElementLookup(val, "."+propertyPath)
   138  		if err != nil {
   139  			err = errVarCannotGetProperty(split[0], propertyPath, err)
   140  		}
   141  		return value, err
   142  	}
   143  }
   144  
   145  func (v *Variables) getValue(name string) (interface{}, error) {
   146  	switch name {
   147  	case ENV:
   148  		return getEnvVarValue(v), nil
   149  
   150  	case GLOBAL:
   151  		return getGlobalValues(), nil
   152  
   153  	case MODULE:
   154  		return ModuleVariables.GetValues(v.process), nil
   155  
   156  	case SELF:
   157  		return getVarSelf(v.process), nil
   158  
   159  	case ARGV, ARGS:
   160  		return getVarArgs(v.process), nil
   161  
   162  	case PARAMS:
   163  		return v.process.Scope.Parameters.StringArray(), nil
   164  
   165  	case MUREX_EXE:
   166  		return getVarMurexExeValue()
   167  
   168  	case MUREX_ARGV, MUREX_ARGS:
   169  		return getVarMurexArgs(), nil
   170  
   171  	case HOSTNAME:
   172  		return getHostname(), nil
   173  
   174  	case PWD:
   175  		return getPwdValue()
   176  
   177  	case COLUMNS:
   178  		return getVarColumnsValue(), nil
   179  
   180  	case "0":
   181  		return v.process.Scope.Name.String(), nil
   182  	}
   183  
   184  	if i, err := strconv.Atoi(name); err == nil && i > 0 {
   185  		s, err := v.process.Scope.Parameters.String(i - 1)
   186  		if err != nil {
   187  			return nil, nil
   188  		}
   189  		return s, nil
   190  	}
   191  
   192  	if v.global {
   193  		return v.getValueValue(name), nil
   194  	}
   195  
   196  	value := v.getValueValue(name)
   197  	if value != nil {
   198  		return value, nil
   199  	}
   200  
   201  	value = GlobalVariables.getValueValue(name)
   202  	if value != nil {
   203  		return value, nil
   204  	}
   205  
   206  	// variable not found so lets fallback to the environmental variables
   207  	s, exists := os.LookupEnv(name)
   208  	if exists {
   209  		return v.getEnvValueValue(name, s)
   210  	}
   211  
   212  	strictVars, err := v.process.Config.Get("proc", "strict-vars", "bool")
   213  	if err != nil || strictVars.(bool) {
   214  		return nil, errVarNotExist(name)
   215  	}
   216  	return nil, nil
   217  }
   218  
   219  func (v *Variables) getValueValue(name string) interface{} {
   220  	v.mutex.Lock()
   221  	variable := v.vars[name]
   222  	if variable == nil {
   223  		v.mutex.Unlock()
   224  		return nil
   225  	}
   226  
   227  	if variable.IsInterface {
   228  		value := variable.Value.(MxInterface).GetValue()
   229  
   230  		v.mutex.Unlock()
   231  		return value
   232  	}
   233  
   234  	value := variable.Value
   235  
   236  	v.mutex.Unlock()
   237  	return value
   238  }
   239  
   240  func (v *Variables) getEnvValueValue(name, str string) (interface{}, error) {
   241  	dt := getEnvVarDataType(name)
   242  	if dt == types.String {
   243  		return str, nil
   244  	}
   245  
   246  	value, err := UnmarshalDataBuffered(v.process, []byte(str), dt)
   247  	return value, err
   248  }
   249  
   250  // GetString returns a string representation of the data stored in the requested variable
   251  func (v *Variables) GetString(path string) (string, error) {
   252  	if path == "." {
   253  		return v.getString(DOT)
   254  	}
   255  
   256  	split := strings.Split(path, ".")
   257  	switch len(split) {
   258  	case 0:
   259  		return "", errVarZeroLengthPath(path)
   260  	case 1:
   261  		return v.getString(split[0])
   262  	default:
   263  		val, err := v.getValue(split[0])
   264  		if err != nil {
   265  			return "", err
   266  		}
   267  
   268  		propertyPath := strings.Join(split[1:], ".")
   269  		val, err = ElementLookup(val, "."+propertyPath)
   270  		if err != nil {
   271  			return "", errVarCannotGetProperty(split[0], propertyPath, err)
   272  		}
   273  
   274  		switch val.(type) {
   275  		case int, float64, string, bool, nil:
   276  			s, err := types.ConvertGoType(val, types.String)
   277  			if err != nil {
   278  				return "", err
   279  			}
   280  			return s.(string), nil
   281  		default:
   282  			dataType := v.GetDataType(split[0])
   283  			b, err := MarshalData(v.process, dataType, val)
   284  			return string(b), err
   285  		}
   286  	}
   287  }
   288  
   289  func (v *Variables) getString(name string) (string, error) {
   290  	switch name {
   291  	case ENV:
   292  		b, err := json.Marshal(getEnvVarString(), v.process.Stdout.IsTTY())
   293  		return string(b), err
   294  
   295  	case GLOBAL:
   296  		b, err := json.Marshal(getGlobalValues(), v.process.Stdout.IsTTY())
   297  		return string(b), err
   298  
   299  	case MODULE:
   300  		b, err := json.Marshal(ModuleVariables.GetValues(v.process), v.process.Stdout.IsTTY())
   301  		return string(b), err
   302  
   303  	case SELF:
   304  		b, err := json.Marshal(getVarSelf(v.process), v.process.Stdout.IsTTY())
   305  		return string(b), err
   306  
   307  	case ARGV, ARGS:
   308  		b, err := json.Marshal(getVarArgs(v.process), v.process.Stdout.IsTTY())
   309  		return string(b), err
   310  
   311  	case PARAMS:
   312  		b, err := json.Marshal(v.process.Scope.Parameters.StringArray(), v.process.Stdout.IsTTY())
   313  		return string(b), err
   314  
   315  	case MUREX_EXE:
   316  		return os.Executable()
   317  
   318  	case MUREX_ARGV, MUREX_ARGS:
   319  		b, err := json.Marshal(getVarMurexArgs(), v.process.Stdout.IsTTY())
   320  		return string(b), err
   321  
   322  	case HOSTNAME:
   323  		return getHostname(), nil
   324  
   325  	case PWD:
   326  		return os.Getwd()
   327  
   328  	case COLUMNS:
   329  		return strconv.Itoa(getVarColumnsValue()), nil
   330  
   331  	case "0":
   332  		return v.process.Scope.Name.String(), nil
   333  	}
   334  
   335  	if i, err := strconv.Atoi(name); err == nil && i > 0 {
   336  		s, err := v.process.Scope.Parameters.String(i - 1)
   337  		if err != nil {
   338  			return "", errVarNoParam(i, err)
   339  		}
   340  		return s, nil
   341  	}
   342  
   343  	if v.global {
   344  		val, _ := v.getStringValue(name)
   345  		return val, nil
   346  	}
   347  
   348  	s, exists := v.getStringValue(name)
   349  	if exists {
   350  		return s, nil
   351  	}
   352  
   353  	s, exists = GlobalVariables.getStringValue(name)
   354  	if exists {
   355  		return s, nil
   356  	}
   357  
   358  	// variable not found so lets fallback to the environmental variables
   359  	s, exists = os.LookupEnv(name)
   360  
   361  	strictVars, err := v.process.Config.Get("proc", "strict-vars", "bool")
   362  	if (err != nil || strictVars.(bool)) && !exists {
   363  		return "", errVarNotExist(name)
   364  	}
   365  
   366  	return s, nil
   367  }
   368  
   369  func (v *Variables) getStringValue(name string) (string, bool) {
   370  	v.mutex.Lock()
   371  	variable := v.vars[name]
   372  	if variable == nil {
   373  		v.mutex.Unlock()
   374  		return "", false
   375  	}
   376  
   377  	if variable.IsInterface {
   378  		s := variable.Value.(MxInterface).GetString()
   379  
   380  		v.mutex.Unlock()
   381  		return s, true
   382  	}
   383  
   384  	s := variable.String
   385  	v.mutex.Unlock()
   386  	return s, true
   387  }
   388  
   389  // GetDataType returns the data type of the variable stored in the referenced VarTable
   390  func (v *Variables) GetDataType(path string) string {
   391  	if path == "." {
   392  		return v.getDataType(DOT)
   393  	}
   394  
   395  	split := strings.Split(path, ".")
   396  	switch len(split) {
   397  	case 0:
   398  		return ""
   399  	case 1:
   400  		return v.getDataType(split[0])
   401  	default:
   402  		switch split[0] {
   403  		case ENV:
   404  			return getEnvVarDataType(split[1])
   405  		case GLOBAL:
   406  			return getGlobalDataType(split[1])
   407  		case MODULE:
   408  			return ModuleVariables.GetDataType(v.process, split[1])
   409  		}
   410  
   411  		val, err := v.getValue(split[0])
   412  		if err != nil {
   413  			return v.getDataType(split[0])
   414  		}
   415  
   416  		val, err = ElementLookup(val, "."+strings.Join(split[1:], "."))
   417  		if err != nil {
   418  			return v.getDataType(split[0])
   419  		}
   420  
   421  		switch val.(type) {
   422  		case int:
   423  			return types.Integer
   424  		case float64:
   425  			return types.Number
   426  		case string, []byte, []rune:
   427  			return types.String
   428  		case bool:
   429  			return types.Boolean
   430  		case nil:
   431  			return types.Null
   432  		default:
   433  			return v.getDataType(split[0])
   434  		}
   435  	}
   436  }
   437  
   438  func (v *Variables) getDataType(name string) string {
   439  	switch name {
   440  	case ENV, GLOBAL, MODULE:
   441  		return types.Json
   442  
   443  	case SELF:
   444  		return types.Json
   445  
   446  	case ARGV, ARGS:
   447  		return types.Json
   448  
   449  	case PARAMS:
   450  		return types.Json
   451  
   452  	case MUREX_EXE:
   453  		return types.Path
   454  
   455  	case MUREX_ARGV, MUREX_ARGS:
   456  		return types.Json
   457  
   458  	case PWD:
   459  		return types.Path
   460  
   461  	case COLUMNS:
   462  		return types.Integer
   463  
   464  	case "0":
   465  		return types.String
   466  	}
   467  
   468  	if i, err := strconv.Atoi(name); err == nil && i > 0 {
   469  		if i >= v.process.Scope.Parameters.Len() {
   470  			return ""
   471  		}
   472  		return types.String
   473  	}
   474  
   475  	if v.global {
   476  		dt, _ := v.getDataTypeValue(name)
   477  		return dt
   478  	}
   479  
   480  	s, exists := v.getDataTypeValue(name)
   481  	if exists {
   482  		return s
   483  	}
   484  
   485  	s, exists = GlobalVariables.getDataTypeValue(name)
   486  	if exists {
   487  		return s
   488  	}
   489  
   490  	// variable not found so lets fallback to the environmental variables
   491  	_, exists = os.LookupEnv(name)
   492  	if exists {
   493  		return getEnvVarDataType(name)
   494  	}
   495  
   496  	return types.Null
   497  }
   498  
   499  func (v *Variables) getDataTypeValue(name string) (string, bool) {
   500  	v.mutex.Lock()
   501  	variable := v.vars[name]
   502  	if variable == nil {
   503  		v.mutex.Unlock()
   504  		return "", false
   505  	}
   506  
   507  	dt := variable.DataType
   508  	v.mutex.Unlock()
   509  	return dt, true
   510  }
   511  
   512  func getGlobalDataType(name string) (dt string) {
   513  	GlobalVariables.mutex.Lock()
   514  	v := GlobalVariables.vars[name]
   515  	if v != nil {
   516  		dt = v.DataType
   517  	}
   518  	GlobalVariables.mutex.Unlock()
   519  	return
   520  }
   521  
   522  func getEnvVarDataType(name string) string {
   523  	for dt, v := range envDataTypes {
   524  		if lists.Match(v, name) {
   525  			return dt
   526  		}
   527  	}
   528  
   529  	return types.String
   530  }
   531  
   532  func (v *Variables) Set(p *Process, path string, value interface{}, dataType string) error {
   533  	if strings.Contains(path, "[") {
   534  		return errVarCannotUpdateIndexOrElement(path)
   535  	}
   536  
   537  	split := strings.Split(path, ".")
   538  	switch len(split) {
   539  	case 0:
   540  		return errVarZeroLengthPath(path)
   541  	case 1:
   542  		return v.set(p, split[0], value, dataType, nil)
   543  	default:
   544  		variable, err := v.getValue(split[0])
   545  		if err != nil {
   546  			return errVarCannotUpdateNested(split[0], err)
   547  		}
   548  
   549  		variable, err = alter.Alter(p.Context, variable, split[1:], value)
   550  		if err != nil {
   551  			return errVarCannotUpdateNested(split[0], err)
   552  		}
   553  		err = v.set(p, split[0], variable, v.getNestedDataType(split[0], dataType), split[1:])
   554  		if err != nil {
   555  			return errVarCannotUpdateNested(split[0], err)
   556  		}
   557  		return nil
   558  	}
   559  }
   560  
   561  func (v *Variables) getNestedDataType(name string, dataType string) string {
   562  	switch name {
   563  	case GLOBAL, MODULE:
   564  		return dataType
   565  	default:
   566  		return v.GetDataType(name)
   567  	}
   568  }
   569  
   570  // Set writes a variable
   571  func (v *Variables) set(p *Process, name string, value interface{}, dataType string, changePath []string) error {
   572  	switch name {
   573  	case SELF, ARGV, ARGS, PARAMS, MUREX_EXE, MUREX_ARGS, MUREX_ARGV, HOSTNAME, PWD, COLUMNS, "_":
   574  		return errVariableReserved(name)
   575  	case ENV:
   576  		return setEnvVar(value, changePath)
   577  	case GLOBAL:
   578  		return setGlobalVar(p, value, changePath, dataType)
   579  	case MODULE:
   580  		return ModuleVariables.Set(p, value, changePath, dataType)
   581  	case DOT:
   582  		goto notReserved
   583  	}
   584  	for _, r := range name {
   585  		if r < '0' || r > '9' {
   586  			goto notReserved
   587  		}
   588  	}
   589  	return errVariableReserved(name)
   590  
   591  notReserved:
   592  
   593  	fileRef := v.process.FileRef
   594  	if v.global {
   595  		fileRef = p.FileRef
   596  	}
   597  
   598  	mxi := MxInterfaces[dataType]
   599  	if mxi != nil {
   600  		mxvar := v.vars[name]
   601  		if mxvar != nil && mxvar.IsInterface {
   602  
   603  			v.mutex.Lock()
   604  
   605  			err := mxvar.Value.(MxInterface).Set(value, changePath)
   606  			if err != nil {
   607  				v.vars[name].Modify = time.Now()
   608  			}
   609  
   610  			v.mutex.Unlock()
   611  
   612  			return err
   613  		}
   614  
   615  		s, _, err := convertDataType(p, value, dataType, &name)
   616  		if err != nil {
   617  			return err
   618  		}
   619  
   620  		mxi, err := mxi.New(s)
   621  		if err != nil {
   622  			return err
   623  		}
   624  
   625  		v.mutex.Lock()
   626  
   627  		v.vars[name] = &variable{
   628  			Value:       mxi,
   629  			DataType:    dataType,
   630  			Modify:      time.Now(),
   631  			FileRef:     fileRef,
   632  			IsInterface: true,
   633  		}
   634  
   635  		v.mutex.Unlock()
   636  
   637  		return nil
   638  	}
   639  
   640  	s, iface, err := convertDataType(p, value, dataType, &name)
   641  	if err != nil {
   642  		return err
   643  	}
   644  
   645  	v.mutex.Lock()
   646  
   647  	v.vars[name] = &variable{
   648  		Value:    iface,
   649  		String:   s,
   650  		DataType: dataType,
   651  		Modify:   time.Now(),
   652  		FileRef:  fileRef,
   653  	}
   654  
   655  	v.mutex.Unlock()
   656  
   657  	return nil
   658  }
   659  
   660  func setGlobalVar(p *Process, v interface{}, changePath []string, dataType string) (err error) {
   661  	if len(changePath) == 0 {
   662  		return fmt.Errorf("invalid use of $%s. Expecting a global variable name, eg `$%s.example`", GLOBAL, GLOBAL)
   663  	}
   664  
   665  	switch t := v.(type) {
   666  	case map[string]interface{}:
   667  		return GlobalVariables.Set(ShellProcess, changePath[0], t[changePath[0]], dataType)
   668  
   669  	default:
   670  		return fmt.Errorf("expecting a map of global variables. Instead got a %T", t)
   671  	}
   672  }
   673  
   674  func setEnvVar(v interface{}, changePath []string) (err error) {
   675  	var value interface{}
   676  
   677  	if len(changePath) == 0 {
   678  		return fmt.Errorf("invalid use of $%s. Expecting an environmental variable name, eg `$%s.EXAMPLE`", ENV, ENV)
   679  	}
   680  
   681  	switch t := v.(type) {
   682  	case map[string]interface{}:
   683  		value, err = types.ConvertGoType(t[changePath[0]], types.String)
   684  		if err != nil {
   685  			return err
   686  		}
   687  
   688  	default:
   689  		return fmt.Errorf("expecting a map of environmental variables. Instead got a %T", t)
   690  	}
   691  
   692  	return os.Setenv(changePath[0], value.(string))
   693  }
   694  
   695  func convertDataType(p *Process, value interface{}, dataType string, name *string) (string, interface{}, error) {
   696  	var (
   697  		s     string
   698  		iface interface{}
   699  		err   error
   700  	)
   701  
   702  	switch v := value.(type) {
   703  	case float64, int, bool, nil:
   704  		s, err = varConvertPrimitive(value, name)
   705  		iface = value
   706  	case string:
   707  		s = v
   708  		if dataType != types.String && dataType != types.Generic {
   709  			iface, err = varConvertString(p, []byte(v), dataType, name)
   710  		} else {
   711  			iface = s
   712  		}
   713  	case []byte:
   714  		s = string(v)
   715  		if dataType != types.String && dataType != types.Generic {
   716  			iface, err = varConvertString(p, v, dataType, name)
   717  		} else {
   718  			iface = s
   719  		}
   720  	case []rune:
   721  		s = string(v)
   722  		if dataType != types.String && dataType != types.Generic {
   723  			iface, err = varConvertString(p, []byte(string(v)), dataType, name)
   724  		} else {
   725  			iface = s
   726  		}
   727  	default:
   728  		s, err = varConvertInterface(p, v, dataType, name)
   729  		iface = value
   730  	}
   731  	return s, iface, err
   732  }
   733  
   734  func varConvertPrimitive(value interface{}, name *string) (string, error) {
   735  	s, err := types.ConvertGoType(value, types.String)
   736  	if err != nil {
   737  		return "", errVarCannotStoreVariable(*name, err)
   738  	}
   739  	return s.(string), nil
   740  }
   741  
   742  func varConvertString(parent *Process, value []byte, dataType string, name *string) (interface{}, error) {
   743  	UnmarshalData := Unmarshallers[dataType]
   744  
   745  	// no unmarshaller exists so lets just return the bare string
   746  	if UnmarshalData == nil {
   747  		return string(value), nil
   748  	}
   749  
   750  	p := new(Process)
   751  	p.Config = parent.Config
   752  	p.Stdin = streams.NewStdin()
   753  	_, err := p.Stdin.Write([]byte(value))
   754  	if err != nil {
   755  		return nil, errVarCannotStoreVariable(*name, err)
   756  	}
   757  	v, err := UnmarshalData(p)
   758  	if err != nil {
   759  		return nil, errVarCannotStoreVariable(*name, err)
   760  	}
   761  	return v, nil
   762  }
   763  
   764  func varConvertInterface(p *Process, value interface{}, dataType string, name *string) (string, error) {
   765  	MarshalData := Marshallers[dataType]
   766  
   767  	// no marshaller exists so lets just return the bare string
   768  	if MarshalData == nil {
   769  		s, err := types.ConvertGoType(value, types.String)
   770  		if err != nil {
   771  			return "", errVarCannotStoreVariable(*name, err)
   772  		}
   773  		return s.(string), nil
   774  	}
   775  
   776  	b, err := MarshalData(p, value)
   777  	if err != nil {
   778  		return "", errVarCannotStoreVariable(*name, err)
   779  	}
   780  	s, err := types.ConvertGoType(b, types.String)
   781  	if err != nil {
   782  		return "", errVarCannotStoreVariable(*name, err)
   783  	}
   784  	return s.(string), nil
   785  }
   786  
   787  // Unset removes a variable from the table
   788  func (v *Variables) Unset(name string) error {
   789  	v.mutex.Lock()
   790  	variable := v.vars[name]
   791  	if variable == nil {
   792  		v.mutex.Unlock()
   793  		return errVarNotExist(name)
   794  	}
   795  
   796  	delete(v.vars, name)
   797  	v.mutex.Unlock()
   798  	return nil
   799  }
   800  
   801  // Dump returns a map of the structure of all variables in scope
   802  func (v *Variables) Dump() interface{} {
   803  	v.mutex.Lock()
   804  	vars := v.vars // TODO: This isn't thread safe
   805  	v.mutex.Unlock()
   806  
   807  	return vars
   808  }
   809  
   810  // DumpVariables returns a map of the variables and values for all variables
   811  // in scope.
   812  func DumpVariables(p *Process) map[string]interface{} {
   813  	m := make(map[string]interface{})
   814  
   815  	envvars.All(m)
   816  
   817  	GlobalVariables.mutex.Lock()
   818  	for name, v := range GlobalVariables.vars {
   819  		m[name] = v.Value
   820  	}
   821  	GlobalVariables.mutex.Unlock()
   822  
   823  	p.Variables.mutex.Lock()
   824  	for name, v := range p.Variables.vars {
   825  		m[name] = v.Value
   826  	}
   827  	p.Variables.mutex.Unlock()
   828  
   829  	m[SELF], _ = p.Variables.GetValue(SELF)
   830  	m[ARGV], _ = p.Variables.GetValue(ARGV)
   831  	m[PARAMS], _ = p.Variables.GetValue(PARAMS)
   832  	m[MUREX_EXE], _ = p.Variables.GetValue(MUREX_EXE)
   833  	m[MUREX_ARGV], _ = p.Variables.GetValue(MUREX_ARGV)
   834  	m[HOSTNAME], _ = p.Variables.GetValue(HOSTNAME)
   835  	m[PWD], _ = p.Variables.GetValue(PWD)
   836  	m[ENV], _ = p.Variables.GetValue(ENV)
   837  	m[GLOBAL] = ".."
   838  	m[COLUMNS], _ = p.Variables.GetValue(COLUMNS)
   839  
   840  	return m
   841  }