github.com/panekj/cli@v0.0.0-20230304125325-467dd2f3797e/opts/file.go (about) 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 // ErrBadKey typed error for bad environment variable 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 // We trim UTF8 BOM 41 if currentLine == 0 { 42 scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom) 43 } 44 // trim the line from all leading whitespace first 45 line := strings.TrimLeftFunc(string(scannedBytes), unicode.IsSpace) 46 currentLine++ 47 // line is not empty, and not starting with '#' 48 if len(line) > 0 && !strings.HasPrefix(line, "#") { 49 variable, value, hasValue := strings.Cut(line, "=") 50 51 // trim the front of a variable, but nothing else 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 // pass the value through, no trimming 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 // if only a pass-through variable is given, clean it up. 70 lines = append(lines, strings.TrimSpace(variable)+"="+value) 71 } 72 } 73 } 74 } 75 return lines, scanner.Err() 76 }