github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/env/env.go (about)

     1  // Package for processing environment variables.
     2  package env
     3  
     4  // TODO: we need to add tests for this package.
     5  
     6  import (
     7  	"bufio"
     8  	"fmt"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // DefaultEnvVariables sets $PATH and $TERM.
    16  var DefaultEnvVariables = map[string]string{
    17  	"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    18  	"TERM": "xterm",
    19  }
    20  
    21  const whiteSpaces = " \t"
    22  
    23  // ParseSlice parses the specified slice and transforms it into an environment
    24  // map.
    25  func ParseSlice(s []string) (map[string]string, error) {
    26  	env := make(map[string]string, len(s))
    27  	for _, e := range s {
    28  		if err := parseEnv(env, e); err != nil {
    29  			return nil, err
    30  		}
    31  	}
    32  	return env, nil
    33  }
    34  
    35  // Slice transforms the specified map of environment variables into a
    36  // slice. If a value is non-empty, the key and value are joined with '='.
    37  func Slice(m map[string]string) []string {
    38  	env := make([]string, len(m))
    39  	for k, v := range m {
    40  		var s string
    41  		if len(v) > 0 {
    42  			s = fmt.Sprintf("%s=%s", k, v)
    43  		} else {
    44  			s = k
    45  		}
    46  		env = append(env, s)
    47  	}
    48  	return env
    49  }
    50  
    51  // Join joins the two environment maps with override overriding base.
    52  func Join(base map[string]string, override map[string]string) map[string]string {
    53  	if len(base) == 0 {
    54  		return override
    55  	}
    56  	for k, v := range override {
    57  		base[k] = v
    58  	}
    59  	return base
    60  }
    61  
    62  // ParseFile parses the specified path for environment variables and returns them
    63  // as a map.
    64  func ParseFile(path string) (_ map[string]string, err error) {
    65  	env := make(map[string]string)
    66  	defer func() {
    67  		if err != nil {
    68  			err = errors.Wrapf(err, "error parsing env file %q", path)
    69  		}
    70  	}()
    71  
    72  	fh, err := os.Open(path)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	defer fh.Close()
    77  
    78  	scanner := bufio.NewScanner(fh)
    79  	for scanner.Scan() {
    80  		// trim the line from all leading whitespace first
    81  		line := strings.TrimLeft(scanner.Text(), whiteSpaces)
    82  		// line is not empty, and not starting with '#'
    83  		if len(line) > 0 && !strings.HasPrefix(line, "#") {
    84  			if err := parseEnv(env, line); err != nil {
    85  				return nil, err
    86  			}
    87  		}
    88  	}
    89  	return env, scanner.Err()
    90  }
    91  
    92  func parseEnv(env map[string]string, line string) error {
    93  	data := strings.SplitN(line, "=", 2)
    94  
    95  	// catch invalid variables such as "=" or "=A"
    96  	if data[0] == "" {
    97  		return errors.Errorf("invalid environment variable: %q", line)
    98  	}
    99  
   100  	// trim the front of a variable, but nothing else
   101  	name := strings.TrimLeft(data[0], whiteSpaces)
   102  	if strings.ContainsAny(name, whiteSpaces) {
   103  		return errors.Errorf("name %q has white spaces, poorly formatted name", name)
   104  	}
   105  
   106  	if len(data) > 1 {
   107  		env[name] = data[1]
   108  	} else {
   109  		if strings.HasSuffix(name, "*") {
   110  			name = strings.TrimSuffix(name, "*")
   111  			for _, e := range os.Environ() {
   112  				part := strings.SplitN(e, "=", 2)
   113  				if len(part) < 2 {
   114  					continue
   115  				}
   116  				if strings.HasPrefix(part[0], name) {
   117  					env[part[0]] = part[1]
   118  				}
   119  			}
   120  		} else if val, ok := os.LookupEnv(name); ok {
   121  			// if only a pass-through variable is given, clean it up.
   122  			env[name] = val
   123  		}
   124  	}
   125  	return nil
   126  }