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