forked from coop-cloud-mirrors/godotenv
Fix bug where internal unquoted whitespace truncates values (#205)
* Add tests to cover the regression reported in #204 * Add a comment on regex for clarity * Remove some old code that wasn't doing anything * Push _all_ parse code into the parser and get tests calling live code * Add some newline specific tests * Add some YAML tests for the newline/space split bug * Fix incorrect terminating of lines on whitespace * Fix most of the parser regressions * Bring back FOO.BAR names * remove some commented out code
This commit is contained in:
126
godotenv.go
126
godotenv.go
@ -15,12 +15,10 @@ package godotenv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -215,130 +213,6 @@ func readFile(filename string) (envMap map[string]string, err error) {
|
||||
return Parse(file)
|
||||
}
|
||||
|
||||
var exportRegex = regexp.MustCompile(`^\s*(?:export\s+)?(.*?)\s*$`)
|
||||
|
||||
func parseLine(line string, envMap map[string]string) (key string, value string, err error) {
|
||||
if len(line) == 0 {
|
||||
err = errors.New("zero length string")
|
||||
return
|
||||
}
|
||||
|
||||
// ditch the comments (but keep quoted hashes)
|
||||
if strings.Contains(line, "#") {
|
||||
segmentsBetweenHashes := strings.Split(line, "#")
|
||||
quotesAreOpen := false
|
||||
var segmentsToKeep []string
|
||||
for _, segment := range segmentsBetweenHashes {
|
||||
if strings.Count(segment, "\"") == 1 || strings.Count(segment, "'") == 1 {
|
||||
if quotesAreOpen {
|
||||
quotesAreOpen = false
|
||||
segmentsToKeep = append(segmentsToKeep, segment)
|
||||
} else {
|
||||
quotesAreOpen = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(segmentsToKeep) == 0 || quotesAreOpen {
|
||||
segmentsToKeep = append(segmentsToKeep, segment)
|
||||
}
|
||||
}
|
||||
|
||||
line = strings.Join(segmentsToKeep, "#")
|
||||
}
|
||||
|
||||
firstEquals := strings.Index(line, "=")
|
||||
firstColon := strings.Index(line, ":")
|
||||
splitString := strings.SplitN(line, "=", 2)
|
||||
if firstColon != -1 && (firstColon < firstEquals || firstEquals == -1) {
|
||||
//this is a yaml-style line
|
||||
splitString = strings.SplitN(line, ":", 2)
|
||||
}
|
||||
|
||||
if len(splitString) != 2 {
|
||||
err = errors.New("can't separate key from value")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the key
|
||||
key = splitString[0]
|
||||
|
||||
key = strings.TrimPrefix(key, "export")
|
||||
|
||||
key = strings.TrimSpace(key)
|
||||
|
||||
key = exportRegex.ReplaceAllString(splitString[0], "$1")
|
||||
|
||||
// Parse the value
|
||||
value = parseValue(splitString[1], envMap)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
singleQuotesRegex = regexp.MustCompile(`\A'(.*)'\z`)
|
||||
doubleQuotesRegex = regexp.MustCompile(`\A"(.*)"\z`)
|
||||
escapeRegex = regexp.MustCompile(`\\.`)
|
||||
unescapeCharsRegex = regexp.MustCompile(`\\([^$])`)
|
||||
)
|
||||
|
||||
func parseValue(value string, envMap map[string]string) string {
|
||||
|
||||
// trim
|
||||
value = strings.Trim(value, " ")
|
||||
|
||||
// check if we've got quoted values or possible escapes
|
||||
if len(value) > 1 {
|
||||
singleQuotes := singleQuotesRegex.FindStringSubmatch(value)
|
||||
|
||||
doubleQuotes := doubleQuotesRegex.FindStringSubmatch(value)
|
||||
|
||||
if singleQuotes != nil || doubleQuotes != nil {
|
||||
// pull the quotes off the edges
|
||||
value = value[1 : len(value)-1]
|
||||
}
|
||||
|
||||
if doubleQuotes != nil {
|
||||
// expand newlines
|
||||
value = escapeRegex.ReplaceAllStringFunc(value, func(match string) string {
|
||||
c := strings.TrimPrefix(match, `\`)
|
||||
switch c {
|
||||
case "n":
|
||||
return "\n"
|
||||
case "r":
|
||||
return "\r"
|
||||
default:
|
||||
return match
|
||||
}
|
||||
})
|
||||
// unescape characters
|
||||
value = unescapeCharsRegex.ReplaceAllString(value, "$1")
|
||||
}
|
||||
|
||||
if singleQuotes == nil {
|
||||
value = expandVariables(value, envMap)
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
var expandVarRegex = regexp.MustCompile(`(\\)?(\$)(\()?\{?([A-Z0-9_]+)?\}?`)
|
||||
|
||||
func expandVariables(v string, m map[string]string) string {
|
||||
return expandVarRegex.ReplaceAllStringFunc(v, func(s string) string {
|
||||
submatch := expandVarRegex.FindStringSubmatch(s)
|
||||
|
||||
if submatch == nil {
|
||||
return s
|
||||
}
|
||||
if submatch[1] == "\\" || submatch[2] == "(" {
|
||||
return submatch[0][1:]
|
||||
} else if submatch[4] != "" {
|
||||
return m[submatch[4]]
|
||||
}
|
||||
return s
|
||||
})
|
||||
}
|
||||
|
||||
func doubleQuoteEscape(line string) string {
|
||||
for _, c := range doubleQuoteSpecialChars {
|
||||
toReplace := "\\" + string(c)
|
||||
|
Reference in New Issue
Block a user