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 }