Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions godotenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"errors"
"os"
"os/exec"
"regexp"
"strings"
)

Expand Down Expand Up @@ -197,11 +198,11 @@ func parseLine(line string) (key string, value string, err error) {
line = strings.Join(segmentsToKeep, "#")
}

// now split key from value
firstEquals := strings.Index(line, "=")
firstColon := strings.Index(line, ":")
splitString := strings.SplitN(line, "=", 2)

if len(splitString) != 2 {
// try yaml mode!
if firstColon != -1 && (firstColon < firstEquals || firstEquals == -1) {
//this is a yaml-style line
splitString = strings.SplitN(line, ":", 2)
}

Expand All @@ -218,27 +219,39 @@ func parseLine(line string) (key string, value string, err error) {
key = strings.Trim(key, " ")

// Parse the value
value = splitString[1]
value = parseValue(splitString[1])
return
}

func parseValue(value string) string {

// trim
value = strings.Trim(value, " ")

// check if we've got quoted values
if value != "" {
// check if we've got quoted values or possible escapes
if len(value) > 1 {
first := string(value[0:1])
last := string(value[len(value)-1:])
if first == last && strings.ContainsAny(first, `"'`) {
// pull the quotes off the edges
value = strings.Trim(value, `"'`)

// expand quotes
value = strings.Replace(value, `\"`, `"`, -1)
// expand newlines
value = strings.Replace(value, `\n`, "\n", -1)
value = value[1 : len(value)-1]
// handle escapes
escapeRegex := regexp.MustCompile(`\\.`)
value = escapeRegex.ReplaceAllStringFunc(value, func(match string) string {
c := strings.TrimPrefix(match, `\`)
switch c {
case "n":
return "\n"
case "r":
return "\r"
default:
return c
}
})
}
}

return
return value
}

func isIgnoredLine(line string) bool {
Expand Down
18 changes: 18 additions & 0 deletions godotenv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,18 @@ func TestParsing(t *testing.T) {
// parses escaped double quotes
parseAndCompare(t, `FOO="escaped\"bar"`, "FOO", `escaped"bar`)

// parses single quotes inside double quotes
parseAndCompare(t, `FOO="'d'"`, "FOO", `'d'`)

// parses yaml style options
parseAndCompare(t, "OPTION_A: 1", "OPTION_A", "1")

//parses yaml values with equal signs
parseAndCompare(t, "OPTION_A: Foo=bar", "OPTION_A", "Foo=bar")

// parses non-yaml options with colons
parseAndCompare(t, "OPTION_A=1:B", "OPTION_A", "1:B")

// parses export keyword
parseAndCompare(t, "export OPTION_A=2", "OPTION_A", "2")
parseAndCompare(t, `export OPTION_B='\n'`, "OPTION_B", "\n")
Expand Down Expand Up @@ -238,6 +247,15 @@ func TestParsing(t *testing.T) {
parseAndCompare(t, `FOO="ba#r"`, "FOO", "ba#r")
parseAndCompare(t, "FOO='ba#r'", "FOO", "ba#r")

//newlines and backslashes should be escaped
parseAndCompare(t, `FOO="bar\n\ b\az"`, "FOO", "bar\n baz")
parseAndCompare(t, `FOO="bar\\\n\ b\az"`, "FOO", "bar\\\n baz")
parseAndCompare(t, `FOO="bar\\r\ b\az"`, "FOO", "bar\\r baz")

parseAndCompare(t, `="value"`, "", "value")
parseAndCompare(t, `KEY="`, "KEY", "\"")
parseAndCompare(t, `KEY="value`, "KEY", "\"value")

// it 'throws an error if line format is incorrect' do
// expect{env('lol$wut')}.to raise_error(Dotenv::FormatError)
badlyFormattedLine := "lol$wut"
Expand Down