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