...
1 package opts
2
3 import (
4 "bufio"
5 "bytes"
6 "fmt"
7 "os"
8 "strings"
9 "unicode"
10 "unicode/utf8"
11 )
12
13 const whiteSpaces = " \t"
14
15
16 type ErrBadKey struct {
17 msg string
18 }
19
20 func (e ErrBadKey) Error() string {
21 return fmt.Sprintf("poorly formatted environment: %s", e.msg)
22 }
23
24 func parseKeyValueFile(filename string, emptyFn func(string) (string, bool)) ([]string, error) {
25 fh, err := os.Open(filename)
26 if err != nil {
27 return []string{}, err
28 }
29 defer fh.Close()
30
31 lines := []string{}
32 scanner := bufio.NewScanner(fh)
33 currentLine := 0
34 utf8bom := []byte{0xEF, 0xBB, 0xBF}
35 for scanner.Scan() {
36 scannedBytes := scanner.Bytes()
37 if !utf8.Valid(scannedBytes) {
38 return []string{}, fmt.Errorf("env file %s contains invalid utf8 bytes at line %d: %v", filename, currentLine+1, scannedBytes)
39 }
40
41 if currentLine == 0 {
42 scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom)
43 }
44
45 line := strings.TrimLeftFunc(string(scannedBytes), unicode.IsSpace)
46 currentLine++
47
48 if len(line) > 0 && !strings.HasPrefix(line, "#") {
49 variable, value, hasValue := strings.Cut(line, "=")
50
51
52 variable = strings.TrimLeft(variable, whiteSpaces)
53 if strings.ContainsAny(variable, whiteSpaces) {
54 return []string{}, ErrBadKey{fmt.Sprintf("variable '%s' contains whitespaces", variable)}
55 }
56 if len(variable) == 0 {
57 return []string{}, ErrBadKey{fmt.Sprintf("no variable name on line '%s'", line)}
58 }
59
60 if hasValue {
61
62 lines = append(lines, variable+"="+value)
63 } else {
64 var present bool
65 if emptyFn != nil {
66 value, present = emptyFn(line)
67 }
68 if present {
69
70 lines = append(lines, strings.TrimSpace(variable)+"="+value)
71 }
72 }
73 }
74 }
75 return lines, scanner.Err()
76 }
77
View as plain text